diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..03fd78e --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,34 @@ +name: Publish + +on: [push, pull_request] + +jobs: + publish: + runs-on: ${{ matrix.os }} + strategy: + matrix: + python: ['3.14'] + os: [ubuntu-latest] + steps: + - uses: actions/checkout@v7 + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.python }} + + # Install dependencies. + - uses: actions/cache@v6 + name: Python cache with dependencies. + id: python-cache + with: + path: ${{ env.pythonLocation }} + key: ${{ env.pythonLocation }}-${{ matrix.os }}-${{ matrix.python }}-${{ hashFiles('pyproject.toml') }} + - name: Install dependencies. + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip list + + # Publish docs. + - name: Publish docs to gh-pages + run: mkdocs gh-deploy --force diff --git a/.gitignore b/.gitignore index a37d1f4..e57f173 100644 --- a/.gitignore +++ b/.gitignore @@ -1,42 +1,2 @@ -# Ignore docs files -_gh_pages -_site -.ruby-version - -# Numerous always-ignore extensions -*.diff -*.err -*.orig -*.log -*.rej -*.swo -*.swp -*.zip -*.vi -*~ - -# OS or Editor folders -.DS_Store -._* -Thumbs.db -.cache -.project -.settings -.tmproj -*.esproj -nbproject -*.sublime-project -*.sublime-workspace -.idea - -# Komodo -*.komodoproject -.komodotools - -# grunt-html-validation -validation-status.json -validation-report.json - -# Folders to ignore -node_modules -.jekyll-cache \ No newline at end of file +.idea/ +site/ \ No newline at end of file diff --git a/README.md b/README.md index 25f2b43..19905ee 100644 --- a/README.md +++ b/README.md @@ -5,17 +5,45 @@ Website for the XLSForm spec published on [xlsform.org](http://xlsform.org). ### Develop -Install [Jekyll](https://jekyllrb.com/) and run with `jekyll serve` on http://localhost:4000. + +```bash +# Get a copy of the repository. +mkdir -P ~/repos/xlsform-docs +cd ~/repos/xlsform-docs +git clone https://github.com/XLSForm/xlsform.github.io.git repo + +# Create and activate a virtual environment for the install. +python -m venv venv +source venv/bin/activate + +# Install pyodk and its production dependencies. +cd ~/repos/xlsform-docs/repo +pip install -r requirements.txt + +# Build and locally serve the docs to preview while editing. +mkdocs serve + +# Leave the virtualenv. +deactivate +``` + +*Deployment* + +A GitHub actions workflow to publish the docs site is run when a pull request is merged. + ### Edit -All content is in the `_sections` and `_data` folders. +All content is in the `docs` folder. + +- assets: extra files such as example XLSForms. +- theme: custom theme template and theme assets. +- index.md: the main docs page + - the only page to add or modify. + - the theme javascript enables custom Markdown table syntax which mimics spreadsheet document tabs. To use this, add a row where each cell contains `===`, then another row where the first column is the "active" sheet name ("survey", "choices", or "settings"). +- reference.md: links / previews of the reference template XLSForm in Google Sheets. + -### Add a translation +*Localisation* -1. Copy the file in `_sections` that you are translating (and give it a new filename - there are no filename rules, but it's helpful to include the language name, e.g. `home-french.md`). -2. Change the 2-character `lang` value at the top to the new file (e.g. `fr`) and translate the content of the file. -3. Add language columns to the 3 CSV files in the `_data` folder with `::fr` added to the translated columns. Any columns without language tags are included in all languages (ie. the columns "Enketo?", "ODK?"). It is best to not translate those columns. -4. Copy the `en` folder and rename it to the language you are adding, e.g. `fr`. -5. In the new folder, change the `lang` and `title` values at the top of each file. -6. Finally add a translated site description in [_config.yml](./_config.yml) \ No newline at end of file +This documentation is not localised - please open an issue if that is something you or your team would find useful (and can help maintain!). diff --git a/_config.yml b/_config.yml deleted file mode 100644 index 68a3b39..0000000 --- a/_config.yml +++ /dev/null @@ -1,20 +0,0 @@ -# Site settings -title: XLSForm.org -email: info@xlsform.org -description: - en: "This site will help you author XLSForms. XLSForm is a tool used to simplify the creation of forms. XLSForm will convert forms authored in Excel into XForms that can be used with a number of web or mobile platforms." -baseurl: "" -url: "https://xlsform.org" - -# Build settings -markdown: kramdown -permalink: pretty - -collections: -- sections - -g_analytics_token: 'UA-55713660-1' -g_analytics_domain: 'xlsform.org' - -kramdown: - smart_quotes: ["apos", "apos", "quot", "quot"] \ No newline at end of file diff --git a/_includes/alerts/warning.html b/_includes/alerts/warning.html deleted file mode 100644 index 259dfe7..0000000 --- a/_includes/alerts/warning.html +++ /dev/null @@ -1,8 +0,0 @@ -
- - - -
- {{ include.content | strip }} -
-
diff --git a/_includes/content.html b/_includes/content.html deleted file mode 100644 index 7d24a56..0000000 --- a/_includes/content.html +++ /dev/null @@ -1,5 +0,0 @@ -{% for section in site.sections %} -{% if section.lang == page.lang and section.ref == page.ref %} -{{ section.content }} -{% endif %} -{% endfor %} \ No newline at end of file diff --git a/_includes/footer.html b/_includes/footer.html deleted file mode 100644 index e876c37..0000000 --- a/_includes/footer.html +++ /dev/null @@ -1,71 +0,0 @@ - - -{% if site.g_analytics_token %} - {% endif %} \ No newline at end of file diff --git a/_includes/head.html b/_includes/head.html deleted file mode 100644 index 692792b..0000000 --- a/_includes/head.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - {% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %} - - - - - {% assign pages=site.pages | where:"ref", page.ref %} - {% for pg in pages %} - {% if pg.lang != page.lang %} - - {% endif %} - {% endfor %} - - diff --git a/_includes/header.html b/_includes/header.html deleted file mode 100644 index 0e3a083..0000000 --- a/_includes/header.html +++ /dev/null @@ -1,35 +0,0 @@ - diff --git a/_includes/table.html b/_includes/table.html deleted file mode 100644 index 7d603e9..0000000 --- a/_includes/table.html +++ /dev/null @@ -1,35 +0,0 @@ -{% assign langpart = '::' | append: page.lang %} -{% capture columns %} -{% for prop in site.data[include.sheet].first %} - {% if prop[0] contains '::' %} - {% if prop[0] contains langpart %} - {{1}}, - {% else %} - {{0}}, - {% endif %} - {% else %} - {{1}}, - {% endif %} -{% endfor %} -{% endcapture %} -{% assign incl-columns = columns | replace: ' ', '' | strip_newlines | split: ',' %} - - - {% for prop in site.data[include.sheet].first %} - {% if incl-columns[forloop.index0] == '1' %} - - {% endif %} - {% endfor %} - - - {% for row in site.data[include.sheet] %} - - {% for prop in row %} - {% if incl-columns[forloop.index0] == '1' %} - - {% endif %} - {% endfor %} - - {% endfor %} - -
{{prop[0] | replace: langpart, ''}}
{{ prop[1] | xml_escape }}
\ No newline at end of file diff --git a/_layouts/default.html b/_layouts/default.html deleted file mode 100644 index d3a5f98..0000000 --- a/_layouts/default.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - {% include head.html %} - - {% include header.html %} - - {{ content }} - - {% include footer.html %} - - diff --git a/_layouts/fullpage.html b/_layouts/fullpage.html deleted file mode 100644 index 91bac88..0000000 --- a/_layouts/fullpage.html +++ /dev/null @@ -1,11 +0,0 @@ ---- -layout: default ---- - -
-
-
- {{ content }} -
-
-
diff --git a/_layouts/home.html b/_layouts/home.html deleted file mode 100644 index 0bbd80e..0000000 --- a/_layouts/home.html +++ /dev/null @@ -1,16 +0,0 @@ ---- -layout: default ---- - -
-
- -
-
- {{ content }} -
-
-
- - - diff --git a/assets/js/redirect.js b/assets/js/redirect.js deleted file mode 100644 index db77c8a..0000000 --- a/assets/js/redirect.js +++ /dev/null @@ -1,11 +0,0 @@ - -var language = navigator.language.split('-')[0]; -var redirectLanguage = 'en'; - -if (languagesSupported.indexOf(language) !== -1){ - redirectLanguage = language; -} - -var newLoc = '/' + redirectLanguage + location.pathname + location.hash; -window.location = newLoc; -document.querySelector('#redirect').setAttribute('href', newLoc); \ No newline at end of file diff --git a/assets/js/script.js b/assets/js/script.js deleted file mode 100644 index 11c7cd7..0000000 --- a/assets/js/script.js +++ /dev/null @@ -1,158 +0,0 @@ -addNavMenu(); -setHeaderListeners(); -addSheets(); -shuffle(Array.prototype.slice.call(document.querySelectorAll('#tools-that-support-xlsforms + ul > li'))) - .forEach(function (el, index) { - el.style.order = index; - }); - -/** - * Adds listeners to a NodeList. - * - * @param {NodeList} elements list of HTML Elements - * @param {String} event type - */ -function addListeners(elements, event, handler) { - if (elements && elements.length) { - for (var i = 0; i < elements.length; i++) { - elements[i].addEventListener(event, handler); - } - } -} - -/** - * Adds Navigation Menu up to 2 levels deep - */ -function addNavMenu() { - var navListEl = document.createElement('ul') - var sectionHeadings = document.querySelectorAll('h2[id]'); - - console.log('section headings', sectionHeadings); - - for (var i = 0; i < sectionHeadings.length; ++i) { - var subHeadings = []; - var next = sectionHeadings[i].nextSibling; - - while (next && next.nodeName.toLowerCase() !== 'h2') { - if (next.nodeName.toLowerCase() === 'h3' && next.id) { - subHeadings.push(next); - } - next = next.nextSibling; - } - var navSectionEl = navListEl.appendChild(getMenuElement(sectionHeadings[i])); - - if (subHeadings.length > 0) { - var navSubSectionListEl = navSectionEl.appendChild(document.createElement('ul')); - for (var j = 0; j < subHeadings.length; ++j) { - navSubSectionListEl.appendChild(getMenuElement(subHeadings[j])); - } - } - } - - document.querySelector('.sidenav').appendChild(navListEl); -} - -/** - * Creates a menu element from a heading element - * @param {Element} headingEl a heading element - */ -function getMenuElement(headingEl) { - var listItemEl = document.createElement('li'), - linkEl = document.createElement('a'); - - linkEl.setAttribute('href', '#' + headingEl.textContent.toLowerCase().replace(/\s/g, '-')); - linkEl.appendChild(document.createTextNode(headingEl.textContent)); - - listItemEl.appendChild(linkEl); - - return listItemEl; -} - -/** - * Add listeners for header clicks to update URL #hash - */ -function setHeaderListeners() { - var headers = document.querySelectorAll('h1, h2, h3, h4, h5, h6'); - - addClickableClass(headers); - addListeners(headers, 'click', headerClickHandler); -} - -/** - * Looks two levels up for an id element and changes URL hash if present. - * - * @param {Event]} event - */ -function headerClickHandler(event) { - var target = event.currentTarget; - if (target) { - if (target.id) { - setHash(target.id); - } - } -} - -/** - * Sets hash in URL without triggering scrolling. Adds state to history. - * @param {string} hash [description] - */ -function setHash(hash) { - hash = (hash && hash.indexOf('#') !== 0) ? '#' + hash : hash; - history.pushState({}, '', hash); -} - -/** - * Adds class "clickable" to all elements in NodeList. - * - * @param {NodeList} headers list of HTML Elements - */ -function addClickableClass(elements) { - if (elements && elements.length) { - for (var i = 0; i < elements.length; i++) { - elements[i].classList.add('clickable'); - } - } -} - -/** - * Shuffles an array - * @param {} array - */ -function shuffle(array) { - var m = array.length; - - while (m) { - var i = Math.floor(Math.random() * m--); - var t = array[m]; - array[m] = array[i]; - array[i] = t; - } - - return array; -} - -function addSheets(){ - var tfoots = document.querySelectorAll('tfoot'); - - for (var i=0; i< tfoots.length; i++){ - var tds = tfoots[i].querySelectorAll('td'); - sheet = tds[0].textContent; - - tds[0].classList.add('sheets'); - tds[0].textContent = ''; - tds[0].setAttribute('colspan', '3'); - - for (var j=0 ; j < 3 ; j++){ - var span = document.createElement('span'); - var type = ['survey', 'choices', 'settings'][j]; - if (j > 0 && tds[j]) { - tds[j].remove(); - } - span.appendChild(document.createTextNode(type)); - if (sheet === type){ - span.classList.add('active'); - } - tds[0].appendChild(span); - } - } -} \ No newline at end of file diff --git a/CNAME b/docs/CNAME similarity index 100% rename from CNAME rename to docs/CNAME diff --git a/assets/xlsx/cascading_select.xlsx b/docs/assets/xlsx/cascading_select.xlsx similarity index 100% rename from assets/xlsx/cascading_select.xlsx rename to docs/assets/xlsx/cascading_select.xlsx diff --git a/_sections/home-english.md b/docs/index.md similarity index 96% rename from _sections/home-english.md rename to docs/index.md index 39d2049..2c0286c 100644 --- a/_sections/home-english.md +++ b/docs/index.md @@ -1,8 +1,3 @@ ---- -ref: home -lang: en ---- - ## What is an XLSForm? XLSForm is a form standard created to help simplify the authoring of forms in Excel. Authoring is done in a human-readable format using a familiar tool that almost everyone knows - Excel. XLSForms provide a practical standard for sharing and collaborating on authoring forms. They are simple to get started with but allow for the authoring of complex forms by someone familiar with the syntax described below. @@ -55,36 +50,36 @@ One thing to keep in mind when authoring forms in Excel is that the syntax you u ## Question types XLSForm supports a number of question types. These are just some of the options you can enter in the **type** column in the **survey** worksheet in your XLSForm: -| Question type | Answer input | -| ------------------------- | -------------------------------------------------------------------------------------------- | -| integer | Integer (i.e., whole number) input. | -| decimal | Decimal input. | -| range | [Range](#range) input (including rating) | -| text | Free text response. | -| select_one [options] | [Multiple choice](#multiple-choice) question; only one answer can be selected. | -| select_multiple [options] | [Multiple choice](#multiple-choice) question; multiple answers can be selected. | -| select_one_from_file [file]| [Multiple choice from file](#multiple-choice-from-file); only one answer can be selected. | -| select_multiple_from_file [file]| [Multiple choice from file](#multiple-choice-from-file); multiple answers can be selected.| -| rank [options] | [Rank](#rank) question; order a list. | -| note | Display a note on the screen, takes no input. Shorthand for type=text with readonly=true. | -| geopoint | Collect a [single GPS coordinate](#gps). | -| geotrace | Record a [line of two or more GPS coordinates](#gps). | -| geoshape | Record a [polygon of multiple GPS coordinates](#gps); the last point is the same as the first point. | -| date | Date input. | -| time | Time input. | -| dateTime | Accepts a date and a time input. | -| image | Take a picture or upload an [image file](#image). | -| audio | Take an audio recording or upload an audio file. | -| background-audio | Audio is recorded in the background while filling the form. | -| video | Take a video recording or upload a video file. | -| file | Generic file input (txt, pdf, xls, xlsx, doc, docx, rtf, zip) | -| barcode | Scan a barcode, requires the barcode scanner app to be installed. | -| calculate | Perform a calculation; see the [Calculation](#calculation) section below. | -| acknowledge | Acknowledge prompt that sets value to "OK" if selected. | -| hidden | A field with no associated UI element which can be used to store a constant | -| xml-external | Adds a reference to an [external XML data](#external-xml-data) file | - -You can find a full list of question types and the appearances that can modify them in the [template and reference](./ref-table). +| Question type | Answer input | +| ------------------------- |------------------------------------------------------------------------------------------------------| +| integer | Integer (i.e., whole number) input. | +| decimal | Decimal input. | +| range | [Range](#range) input (including rating) | +| text | Free text response. | +| select_one [options] | [Multiple choice](#multiple-choice) question; only one answer can be selected. | +| select_multiple [options] | [Multiple choice](#multiple-choice) question; multiple answers can be selected. | +| select_one_from_file [file]| [Multiple choice from file](#multiple-choice-from-file); only one answer can be selected. | +| select_multiple_from_file [file]| [Multiple choice from file](#multiple-choice-from-file); multiple answers can be selected. | +| rank [options] | [Rank](#rank) question; order a list. | +| note | Display a note on the screen, takes no input. Shorthand for type=text with readonly=true. | +| geopoint | Collect a [single GPS coordinate](#capturing-gps-location) | +| geotrace | Record a [line of two or more GPS coordinates](#capturing-gps-location). | +| geoshape | Record a [polygon of multiple GPS coordinates](#capturing-gps-location); the last point is the same as the first point. | +| date | Date input. | +| time | Time input. | +| dateTime | Accepts a date and a time input. | +| image | Take a picture or upload an [image file](#image). | +| audio | Take an audio recording or upload an audio file. | +| background-audio | Audio is recorded in the background while filling the form. | +| video | Take a video recording or upload a video file. | +| file | Generic file input (txt, pdf, xls, xlsx, doc, docx, rtf, zip) | +| barcode | Scan a barcode, requires the barcode scanner app to be installed. | +| calculate | Perform a calculation; see the [Calculation](#calculation) section below. | +| acknowledge | Acknowledge prompt that sets value to "OK" if selected. | +| hidden | A field with no associated UI element which can be used to store a constant | +| xml-external | Adds a reference to an [external XML data](#external-xml-data) file | + +You can find a full list of question types and the appearances that can modify them in the [template and reference](./reference.md). ### Capturing GPS location A question of type `geopoint` captures the current geolocation from a device. To collect the name and GPS coordinates of a store, you would write the following in your form: @@ -98,12 +93,12 @@ A question of type `geopoint` captures the current geolocation from a device. To To collect a line or shape of GPS coordinates, you can use one of the following: -| type | name | label | hint | -| ---------- | ------------ | --------------------------------------------------------------------------------------- | -| geotrace | pipe | Pipeline| Please walk along the pipeline and record the coordinates of each corner point | -| geoshape | border | Border | Please walk along the border and record the coordinates of each corner point | -| ========== | ============ | ======================================================================================== | -| survey | | | | +| type | name | label | hint | +| ---------- | ------------ | -------- | ------------------------------------------------------------------------------ | +| geotrace | pipe | Pipeline | Please walk along the pipeline and record the coordinates of each corner point | +| geoshape | border | Border | Please walk along the border and record the coordinates of each corner point | +| ========== | ============ | ======== | ============================================================================== | +| survey | | | | You can learn more on the geopoint widget in the [ODK documentation](https://docs.getodk.org/form-question-types/#geopoint-widget). @@ -177,7 +172,7 @@ In general, choice names should be unique within a single-choice list. If two ch If you want question responses to filter the options available in later questions, use a cascading select. For example, if you want to ask which city the respondent lives in, instead of choosing from all cities in the world, you could first ask for their country. Then you could filter the list of states to only show the ones in the selected country, filter districts to only show the ones in the selected state, then filter cities to only show the ones in that district. -To chain or cascade selects, you will need to create a **choice_filter** column in your survey worksheet. The expression in this column will be used to filter down the list of choices for the corresponding select. Any choice for which the expression is **true** will be included. Check out an example XLSForm [here](/assets/xlsx/cascading_select.xlsx). +To chain or cascade selects, you will need to create a **choice_filter** column in your survey worksheet. The expression in this column will be used to filter down the list of choices for the corresponding select. Any choice for which the expression is **true** will be included. Check out an example XLSForm [here](assets/xlsx/cascading_select.xlsx). #### Look up values in a choice list @@ -185,7 +180,9 @@ You can add additional columns to the **choices** sheet and then look up values #### Specify other -{% include alerts/warning.html content="We generally recommend using [relevance](#relevant) to specify your own **other** choice. The shortcut described in this section only works for selects without translations or **choice_filter**s. It uses English for the \"Specify other\" choice which cannot be customized." %} +!!! warning + + We generally recommend using [relevance](#relevant) to specify your own **other** choice. The shortcut described in this section only works for selects without translations or **choice_filter**s. It uses English for the \"Specify other\" choice which cannot be customized. For multiple-choice questions, surveys often include an option of marking **other** when their answer choice is not listed. Then, they are usually asked to specify the other option. XLSForm has a shortcut for doing this by adding **or_other** after the answer choice list name in the survey worksheet. The choices worksheet stays the same. See below: @@ -866,7 +863,7 @@ It’s easy to add multiple languages to a form. You simply have to name your ** | ================= | =========== | ============ | ============ | ========== | | survey | | | | | -You can also add different language columns for hints and media files by using the same `::language (code)` construct, as shown in the example below. See also the [XLSForm reference](./ref-table), which includes a list of all column headers that can accept a language modification. +You can also add different language columns for hints and media files by using the same `::language (code)` construct, as shown in the example below. See also the [XLSForm reference](./reference.md), which includes a list of all column headers that can accept a language modification. | hint::English (en) | hint::Dutch (nl) | image::English (en) | image::Dutch (nl) | | ------------------- | -------------------- | -------------------------- | ------------------------ | @@ -979,7 +976,7 @@ The settings column headings available are: * **instance_name**: Expression using form fields to identify each form submission. [Learn more](#specify-form-submission-name). * **default_language**: In localized forms, this sets which language should be used as the default. The same format as described for [adding translations](#multiple-language-support) should be used, including the language code. * **public_key**: For encryption-enabled forms, this is where the public key is copied and pasted. [Learn more](#encrypted-forms). -* **submission_url**: This url can be used to override the default server where finalized records are submitted. [Learn more](#specify-alternative-server). +* **submission_url**: This url can be used to override the default server where finalized records are submitted. [Learn more](#specify-an-alternative-server). * **style**: For web forms, specify the form style. [Learn more](#multiple-webpage-forms). * **name**: XForms root node name. This is rarely needed, [learn more](#specify-xforms-root-node-name). * **clean_text_values**: This column can be used with values `yes` or `no` to control whether whitespace in the Survey sheet is collapsed (defaults to `yes`). @@ -1146,7 +1143,9 @@ As with the above **survey** columns, the **attribute::** setting can be combine ## Appendix - loading big CSVs -{% include alerts/warning.html content="This section describes less general alternatives to [select_one_from_file](#multiple-choice-from-file) and the [`instance` function](#look-up-values-in-choice-lists-or-attached-files). They may be more performant in some tools that allow filling XLSForms but may not work in others." %} +!!! warning + + This section describes less general alternatives to [select_one_from_file](#multiple-choice-from-file) and the [`instance` function](#look-up-values-in-choice-lists-or-attached-files). They may be more performant in some tools that allow filling XLSForms but may not work in others. ### Data preloading @@ -1167,7 +1166,9 @@ Each csv file should contain at least one column that can be used to uniquely id #### How to pull data from CSV -{% include alerts/warning.html content="If you use [select_one_from_file](#multiple-choice-from-file) to show select options from a file, you should generally use the [`instance` function](#look-up-values-in-choice-lists-or-attached-files) to look values up in that file rather than `pulldata`." %} +!!! warning + + If you use [select_one_from_file](#multiple-choice-from-file) to show select options from a file, you should generally use the [`instance` function](#look-up-values-in-choice-lists-or-attached-files) to look values up in that file rather than `pulldata`. You can be able to pull data from .csv file by including one or more .csv files in your form during the survey time. For each data field that you want to pull into your survey: @@ -1197,7 +1198,9 @@ Click on the link to see an example of a [pre-loading sample form ](https://docs #### Dynamic selects from pre-loaded data -{% include alerts/warning.html content="Use [select_one_from_file](#multiple-choice-from-file) unless you need to use more than 50 thousand options, or will be collecting data on old or low performance devices. This approach is not supported by Enketo web forms." %} +!!! warning + + Use [select_one_from_file](#multiple-choice-from-file) unless you need to use more than 50 thousand options, or will be collecting data on old or low performance devices. This approach is not supported by Enketo web forms. Once your form has one or more pre-loaded .csv files, you can dynamically pull the choice lists for **select_one** and **select_multiple** fields from those .csv files. Multiple-choice fields with dynamic choice lists follow the same general syntax as regular, static select_one and select_multiple fields as previously covered in the [Multiple choice questions](#multiple-choice) section. @@ -1279,7 +1282,9 @@ Additional notes on usage: ### Database-backed "fast external itemsets" -{% include alerts/warning.html content="We generally recommend using [select_one_from_file](multiple-choice-from-file) unless you need to use more than 50k rows or very old devices. This approach is not supported by Enketo web forms." %} +!!! warning + + We generally recommend using [select_one_from_file](#multiple-choice-from-file) unless you need to use more than 50k rows or very old devices. This approach is not supported by Enketo web forms. Enabling external selects is straightforward. - Instead of **select_one** for the prompt type, use **select_one_external**. diff --git a/_sections/ref-table-english.md b/docs/reference.md similarity index 98% rename from _sections/ref-table-english.md rename to docs/reference.md index c521b24..aff4690 100644 --- a/_sections/ref-table-english.md +++ b/docs/reference.md @@ -1,8 +1,3 @@ ---- -ref: ref-table -lang: en ---- - It can be helpful to use a template to build an XLSForm rather than starting from a blank document. The template provided below includes some helpful styling rules as well as extensive reference that is kept updated as XLSForm and the tools that use it evolve. You can use it to see which functionality is supported by Collect and Enketo and get ideas on how to meet your form design goals. * [Google Sheets](https://docs.google.com/spreadsheets/d/1v9Bumt3R0vCOGEKQI6ExUf2-8T72-XXp_CbKKTACuko/edit#gid=1052905058) (use `File > Make a copy`) diff --git a/assets/css/main.css b/docs/theme/main.css similarity index 97% rename from assets/css/main.css rename to docs/theme/main.css index 586df2d..be449df 100644 --- a/assets/css/main.css +++ b/docs/theme/main.css @@ -552,7 +552,7 @@ table tr td { display: flex !important; } -.alert { +.admonition { position: relative; padding: .75rem 1.25rem; margin-bottom: 1rem; @@ -560,7 +560,7 @@ table tr td { border-radius: .25rem; } -.alert-warning { +.warning { color: #856404; background-color: #fff3cd; border-color: #ffeeba; @@ -568,4 +568,15 @@ table tr td { .align-items-center { align-items: center !important; -} \ No newline at end of file +} + +.admonition.warning > .admonition-title::before { + content: ""; + display: inline-block; + width: 1em; + height: 1em; + margin-right: .5em; + background: currentColor; + -webkit-mask: url("../warning.svg") center / contain no-repeat; + mask: url("../warning.svg") center / contain no-repeat; +} diff --git a/docs/theme/main.html b/docs/theme/main.html new file mode 100644 index 0000000..bbb05fb --- /dev/null +++ b/docs/theme/main.html @@ -0,0 +1,178 @@ + + + + + + + {% if page.title %} + {{ page.title }}{% if page.title != config.site_name %} — {{ config.site_name }}{% endif %} + {% else %} + {{ config.site_name }} + {% endif %} + + + {%- for path in config.extra_css %} + + {%- endfor %} + + + + +{% if page.is_homepage %} + +
+ +
+
+ {{ page.content }} +
+
+
+ +{% else %} + +
+
+
+ {{ page.content }} +
+
+
+ +{% endif %} + + + +{%- for script in config.extra_javascript %} +{{ script | script_tag }} +{%- endfor %} + +{% if config.extra.g_analytics_token %} + +{% endif %} + + + diff --git a/docs/theme/main.js b/docs/theme/main.js new file mode 100644 index 0000000..02cfc3f --- /dev/null +++ b/docs/theme/main.js @@ -0,0 +1,64 @@ +document.addEventListener('DOMContentLoaded', function () { + // Make headings clickable and update window URL with clicked heading. + document.querySelectorAll('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]') + .forEach(function (heading) { + heading.classList.add('clickable'); + + heading.addEventListener('click', function () { + history.pushState({}, '', '#' + heading.id); + }); + }); + + // Replace last two rows of tables with a Excel lookalike tabs. + document.querySelectorAll('table').forEach(function (table) { + var rows = Array.from(table.querySelectorAll('tbody tr')); + + for (var i = 0; i < rows.length - 1; i++) { + var cells = Array.from(rows[i].querySelectorAll('td')); + + if ( + cells.length && + cells.every(function (cell) { + return /^=+$/.test(cell.textContent.trim()); + }) + ) { + var sheetCell = rows[i + 1].querySelector('td'); + + if (!sheetCell) { + continue; + } + + var sheet = sheetCell.textContent.trim(); + + var tfoot = document.createElement('tfoot'); + var tr = document.createElement('tr'); + var td = document.createElement('td'); + + td.className = 'sheets'; + td.colSpan = table.rows[0].cells.length; + + ['survey', 'choices', 'settings'].forEach(function (name) { + var span = document.createElement('span'); + + span.textContent = name; + + if (name === sheet) { + span.className = 'active'; + } + + td.appendChild(span); + }); + + tr.appendChild(td); + tfoot.appendChild(tr); + + rows[i].remove(); + rows[i + 1].remove(); + + table.appendChild(tfoot); + + break; + } + } + }); +}); diff --git a/docs/theme/warning.svg b/docs/theme/warning.svg new file mode 100644 index 0000000..50e1752 --- /dev/null +++ b/docs/theme/warning.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/en/index.md b/en/index.md deleted file mode 100644 index d83279a..0000000 --- a/en/index.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -layout: home -lang: en -ref: home -title: XLSForm Docs ---- - -{% include content.html %} diff --git a/en/ref-table.md b/en/ref-table.md deleted file mode 100644 index ae6b46e..0000000 --- a/en/ref-table.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -layout: fullpage -lang: en -ref: ref-table -title: Reference and Template ---- -## {{page.title}} -{% include content.html %} diff --git a/index.html b/index.html deleted file mode 100644 index 8c8bff6..0000000 --- a/index.html +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: fullpage ---- - -

Redirecting…

-Click here if you are not redirected. - \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..c504568 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,45 @@ +# ------------------------------ +# Content / Context +# ------------------------------ +site_name: XLSForm.org +site_description: > + This site will help you author XLSForms. + XLSForm is a tool used to simplify the creation of forms. + XLSForm will convert forms authored in Excel into XForms + that can be used with a number of web or mobile platforms. + +extra: + email: info@xlsform.org + github_url: https://github.com/XLSForm/xlsform.github.io + github_username: XLSForm + g_analytics_token: 'UA-55713660-1' + g_analytics_domain: 'xlsform.org' + +nav: + - Home: index.md + - Reference: reference.md + + +# ------------------------------ +# Appearance / Rendering +# ------------------------------ +theme: + name: null + custom_dir: docs/theme + locale: en + +extra_css: + - main.css + +extra_javascript: + - main.js + +# Empty list to disable default "search" plugin. +plugins: [] + +markdown_extensions: + - admonition + - attr_list + - tables + - toc: + permalink: false diff --git a/ref-table.html b/ref-table.html deleted file mode 100644 index 8c8bff6..0000000 --- a/ref-table.html +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: fullpage ---- - -

Redirecting…

-Click here if you are not redirected. - \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..6b64d7a --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +mkdocs==1.6.1 \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml deleted file mode 100644 index ae92927..0000000 --- a/sitemap.xml +++ /dev/null @@ -1,18 +0,0 @@ ---- -layout: -permalink: /sitemap.xml ---- - - - {% for page in site.pages %} - {% if page.id contains "404" or page.ref == nil %}{% else %} - - {{site.url}}{{ page.url }} - {% assign versions=site.pages | where:"ref", page.ref %} - {% for version in versions %} - - {% endfor %} - - {% endif %} - {% endfor %} - \ No newline at end of file