From e46506b4ceccc52abea43f049cd851851ef09673 Mon Sep 17 00:00:00 2001 From: Robin Stevens Date: Mon, 27 Nov 2017 17:58:04 +0100 Subject: [PATCH 001/342] Typo (#1357) --- docs/user-guide/custom-themes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user-guide/custom-themes.md b/docs/user-guide/custom-themes.md index 34da869d62..dce0998a5d 100644 --- a/docs/user-guide/custom-themes.md +++ b/docs/user-guide/custom-themes.md @@ -365,7 +365,7 @@ makes no modifications to the Jinja environment. A complete solution using the provided index file is the responsability of the theme. When `search_index_only` is set to `false`, then the search plugin modifies the -Jinja environment by adding its own `temaplates` directory (with a lower +Jinja environment by adding its own `templates` directory (with a lower precedence than the theme) and adds its scripts to the `extra_javascript` config setting. From 4c7c69cd3a4eb8c0fd2838e26f8f9031fe80f6cf Mon Sep 17 00:00:00 2001 From: Matthew Schmoyer Date: Wed, 29 Nov 2017 19:57:03 -0700 Subject: [PATCH 002/342] Fix typo with plugins module name --- docs/user-guide/plugins.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user-guide/plugins.md b/docs/user-guide/plugins.md index def5e6b760..5860096cd7 100644 --- a/docs/user-guide/plugins.md +++ b/docs/user-guide/plugins.md @@ -59,7 +59,7 @@ points to it. ### BasePlugin -A subclass of `mkdocs.pluhgins.BasePlugin` should define the behavior of the plugin. +A subclass of `mkdocs.plugins.BasePlugin` should define the behavior of the plugin. The class generally consists of actions to perform on specific events in the build process as well as a configuration scheme for the plugin. From dfb50c9160fe53d6ed1a5839fad2d555ecf25003 Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Thu, 7 Dec 2017 11:32:29 -0500 Subject: [PATCH 003/342] Remove PyPI Deployment Docs. PyPI's documentation hosting has been deprecated, and in fact, it is no longer possable to upload documentation to PyPI. Therefore, it is a disservice to our users to document how to deploy to that service. See the mailing list dicussions for the official announcement: https://mail.python.org/pipermail/distutils-sig/2015-May/026327.html https://mail.python.org/pipermail/distutils-sig/2015-May/026381.html --- docs/user-guide/deploying-your-docs.md | 36 -------------------------- 1 file changed, 36 deletions(-) diff --git a/docs/user-guide/deploying-your-docs.md b/docs/user-guide/deploying-your-docs.md index 9fc9736827..2c14f5e6e5 100644 --- a/docs/user-guide/deploying-your-docs.md +++ b/docs/user-guide/deploying-your-docs.md @@ -58,42 +58,6 @@ public repository. [features]: http://read-the-docs.readthedocs.io/en/latest/features.html [theme]: /user-guide/styling-your-docs.md -## PyPI - -If you maintain a [Python] project which is hosted on the [Python Package -Index][PyPI] (PyPI), you can use the hosting provided at [pythonhosted.org] to -host documentation for your project. Run the following commands from your -project's root directory to upload your documentation: - -```sh -mkdocs build -python setup.py upload_docs --upload-dir=site -``` - -Your documentation will be hosted at `https://pythonhosted.org//` -where `` is the name you used to register your project with PyPI. - -There are a few prerequisites for the above to work: - -1. You must be using [Setuptools] in your `setup.py` script ([Distutils] does - not offer an `upload_docs` command). -1. Your project must already be registered with PyPI (use `python setup.py - register`). -1. Your `mkdocs.yml` config file and your "docs" directory (value assigned to - the [docs_dir] configuration option) are presumed to be in the root directory - of your project alongside your `setup.py` script. -1. It is assumed that the default value (`"site"`) is assigned to the [site_dir] - configuration option in your `mkdocs.yaml` config file. If you have set a - different value, assign that value to the `--upload-dir` option. - -[Python]: http://www.python.org/ -[PyPI]: https://pypi.python.org/pypi -[pythonhosted.org]: https://pythonhosted.org/ -[Setuptools]: https://pythonhosted.org/setuptools/ -[Distutils]: https://docs.python.org/2/distutils/ -[docs_dir]: configuration.md#docs_dir -[site_dir]: configuration.md#site_dir - ## Other Providers Any hosting provider which can serve static files can be used to serve From b9b37491e9d693e93f65b00c16613a9611b36cbf Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Thu, 7 Dec 2017 11:39:22 -0500 Subject: [PATCH 004/342] Update Python-Markdown links. Python-Markdown has moved its documentation to https://python-markdown.github.io/. See Python-Markdown/markdown#601 for details. The docs now point to the new location. The Python-Markdown GitHub repo was also moved to https://github.com/Python-Markdown/markdown so any links to the repo have been updated as well. --- docs/user-guide/configuration.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/user-guide/configuration.md b/docs/user-guide/configuration.md index 018fd6e31a..0c04728e03 100644 --- a/docs/user-guide/configuration.md +++ b/docs/user-guide/configuration.md @@ -447,11 +447,11 @@ plugins: [] [custom themes]: custom-themes.md [variables that are available]: custom-themes.md#template-variables -[pymdk-extensions]: https://pythonhosted.org/Markdown/extensions/index.html -[pymkd]: https://pythonhosted.org/Markdown/ -[smarty]: https://pythonhosted.org/Markdown/extensions/smarty.html -[exts]:https://pythonhosted.org/Markdown/extensions/index.html -[3rd]: https://github.com/waylan/Python-Markdown/wiki/Third-Party-Extensions +[pymdk-extensions]: https://python-markdown.github.io/extensions/ +[pymkd]: https://python-markdown.github.io/ +[smarty]: https://python-markdown.github.io/extensions/smarty/ +[exts]: https://python-markdown.github.io/extensions/ +[3rd]: https://github.com/Python-Markdown/markdown/wiki/Third-Party-Extensions [configuring pages and navigation]: writing-your-docs.md#configure-pages-and-navigation [theme_dir]: styling-your-docs.md#using-the-theme_dir [styling your docs]: styling-your-docs.md From ab0b5824d063034dc029e411ed64f9d4841f1b20 Mon Sep 17 00:00:00 2001 From: Brendan Abbott Date: Tue, 19 Dec 2017 02:22:10 +1000 Subject: [PATCH 005/342] Update Release Notes regarding Search plugin. (#1364) Outline that the path where the search index is written to has changed slightly from `mkdocs/` to `search/` --- docs/about/release-notes.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md index bce82aacc2..9229024e34 100644 --- a/docs/about/release-notes.md +++ b/docs/about/release-notes.md @@ -44,10 +44,12 @@ own custom behaviors. See the included documentation for a full explanation of the API. The previously built-in search functionality has been removed and wrapped in a -plugin (named "search") with no changes in behavior. If no plugins setting is -defined in the config, then the `search` plugin will be included by default. -See the [configuration][plugin_config] documentation for information on -overriding the default. +plugin (named "search") with no changes in behavior. When MkDocs builds, the +search index is now written to `search/search_index.json` instead of +`mkdocs/search_index.json`. If no plugins setting is defined in the config, +then the `search` plugin will be included by default. See the +[configuration][plugin_config] documentation for information on overriding the +default. [Plugin API]: ../user-guide/plugins.md [plugin_config]: ../user-guide/configuration.md#plugins From 8d4a883d8952f026d4e52f1a296d98a6d58a94ad Mon Sep 17 00:00:00 2001 From: Alexandre ZANNI Date: Fri, 29 Dec 2017 13:35:18 +0100 Subject: [PATCH 006/342] change theme_dir to custom_dir --- docs/user-guide/custom-themes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user-guide/custom-themes.md b/docs/user-guide/custom-themes.md index dce0998a5d..aa3aa5bc7d 100644 --- a/docs/user-guide/custom-themes.md +++ b/docs/user-guide/custom-themes.md @@ -20,7 +20,7 @@ and their usage. ## Creating a custom theme The bare minimum required for a custom theme is a `main.html` [Jinja2 template] -file. This should be placed in a directory which will be the `theme_dir` and it +file. This should be placed in a directory which will be the `custom_dir` and it should be created next to the `mkdocs.yml` configuration file. Within `mkdocs.yml`, specify the theme `custom_dir` option and set it to the name of the directory containing `main.html`. For example, given this example project From fc2f105bad486aa50af280a015d727bf4575296c Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Sun, 31 Dec 2017 22:19:58 -0500 Subject: [PATCH 007/342] Document how to generate/install manpages. (#1302) Fixes #686. --- docs/index.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index d7e3eb8aec..f92f1820ea 100644 --- a/docs/index.md +++ b/docs/index.md @@ -118,6 +118,19 @@ $ mkdocs --version mkdocs, version 0.15.3 ``` +!!! Note + If you would like manpages installed for MkDocs, the [click-man] tool can + generate and install them for you. Simply run the following two commands: + + pip install click-man + click-man --target path/to/man/pages mkdocs + + See the [click-man documentation] for an explaination of why manpages are + not automaticaly generated and installed by pip. + +[click-man]: https://github.com/click-contrib/click-man +[click-man documentation]: https://github.com/click-contrib/click-man#automatic-man-page-installation-with-setuptools-and-pip + !!! Note If you are using Windows, some of the above commands may not work out-of-the-box. @@ -344,4 +357,4 @@ the MkDocs IRC channel `#mkdocs` on freenode. [pip]: http://pip.readthedocs.io/en/stable/installing/ [Python]: https://www.python.org/ [site_name]: user-guide/configuration/#site_name -[theme]: user-guide/configuration/#theme \ No newline at end of file +[theme]: user-guide/configuration/#theme From d00a8e52f5694889f9e9050f16b69a25dbdc2ca6 Mon Sep 17 00:00:00 2001 From: brandongc Date: Fri, 5 Jan 2018 11:01:34 -0800 Subject: [PATCH 008/342] Refactor `utils.copy_media_files` for more flexibility (#1370) --- docs/about/release-notes.md | 4 ++++ mkdocs/utils/__init__.py | 34 ++++++++++++++++++---------------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md index 9229024e34..08bad39bae 100644 --- a/docs/about/release-notes.md +++ b/docs/about/release-notes.md @@ -21,6 +21,10 @@ The current and past members of the MkDocs team. * [@d0ugal](https://github.com/d0ugal/) * [@waylan](https://github.com/waylan/) +## Development Version + +* Refactor `copy_media_files` util function for more flexibility (#1370). + ## Version 0.17.2 (2017-11-15) * Bugfix: Correct `extra_*` config setting regressions (#1335 & #1336). diff --git a/mkdocs/utils/__init__.py b/mkdocs/utils/__init__.py index a252beba8b..47901f7c5b 100644 --- a/mkdocs/utils/__init__.py +++ b/mkdocs/utils/__init__.py @@ -41,6 +41,14 @@ log = logging.getLogger(__name__) +markdown_extensions = [ + '.markdown', + '.mdown', + '.mkdn', + '.mkd', + '.md' +] + def yaml_load(source, loader=yaml.Loader): """ @@ -145,11 +153,13 @@ def clean_directory(directory): os.unlink(path) -def copy_media_files(from_dir, to_dir, exclude=None, dirty=False): +def copy_media_files(from_dir, to_dir, + exclude=['*{0}'.format(x) for x in markdown_extensions], dirty=False): """ Recursively copy all files except markdown and exclude[ed] files into another directory. `exclude` accepts a list of Unix shell-style wildcards (`['*.py', '*.pyc']`). + Note that `exclude` only operates on file names, not directories. """ for (source_dir, dirnames, filenames) in os.walk(from_dir, followlinks=True): @@ -168,15 +178,14 @@ def copy_media_files(from_dir, to_dir, exclude=None, dirty=False): dirnames[:] = [d for d in dirnames if not d.startswith('.')] for filename in filenames: - if not is_markdown_file(filename): - source_path = os.path.join(source_dir, filename) - output_path = os.path.join(output_dir, filename) + source_path = os.path.join(source_dir, filename) + output_path = os.path.join(output_dir, filename) - # Do not copy when using --dirty if the file has not been modified - if dirty and (modified_time(source_path) < modified_time(output_path)): - continue + # Do not copy when using --dirty if the file has not been modified + if dirty and (modified_time(source_path) < modified_time(output_path)): + continue - copy_file(source_path, output_path) + copy_file(source_path, output_path) def get_html_path(path): @@ -221,14 +230,7 @@ def is_markdown_file(path): http://superuser.com/questions/249436/file-extension-for-markdown-files """ - ext = os.path.splitext(path)[1].lower() - return ext in [ - '.markdown', - '.mdown', - '.mkdn', - '.mkd', - '.md', - ] + return any(fnmatch.fnmatch(path.lower(), '*{0}'.format(x)) for x in markdown_extensions) def is_css_file(path): From 47afdfd77ef5c1974ce563e33bab96b6c458f4f6 Mon Sep 17 00:00:00 2001 From: Yeray Diaz Diaz Date: Sat, 20 Jan 2018 21:22:28 +0000 Subject: [PATCH 009/342] Prevent reinjection of extra Javascript files on reload (#1388) --- mkdocs/contrib/legacy_search/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mkdocs/contrib/legacy_search/__init__.py b/mkdocs/contrib/legacy_search/__init__.py index e00f7322fe..ccb3b9dca0 100644 --- a/mkdocs/contrib/legacy_search/__init__.py +++ b/mkdocs/contrib/legacy_search/__init__.py @@ -22,8 +22,9 @@ def on_config(self, config, **kwargs): if not ('search_index_only' in config['theme'] and config['theme']['search_index_only']): path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates') config['theme'].dirs.append(path) - config['extra_javascript'].append('search/require.js') - config['extra_javascript'].append('search/search.js') + for extra_js in ('search/require.js', 'search/search.js'): + if extra_js not in config['extra_javascript']: + config['extra_javascript'].append(extra_js) return config def on_pre_build(self, config, **kwargs): From cd396ada133caa39542ef78d411ffa4e9c31bfd8 Mon Sep 17 00:00:00 2001 From: Yeray Diaz Diaz Date: Sat, 20 Jan 2018 21:24:26 +0000 Subject: [PATCH 010/342] Remove addition of `clicky` class to body and animations. (#1387) --- mkdocs/themes/mkdocs/css/base.css | 27 +-------------------------- mkdocs/themes/mkdocs/js/base.js | 7 +------ 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/mkdocs/themes/mkdocs/css/base.css b/mkdocs/themes/mkdocs/css/base.css index ea9f2e90d0..34de0b674a 100644 --- a/mkdocs/themes/mkdocs/css/base.css +++ b/mkdocs/themes/mkdocs/css/base.css @@ -39,13 +39,7 @@ ul.nav .main { /* * The code below adds some padding to the top of the current anchor target so * that, when navigating to it, the header isn't hidden by the navbar at the - * top. This is especially complicated because we want to *remove* the padding - * after navigation so that hovering over the header shows the permalink icon - * correctly. Thus, we create a CSS animation to remove the extra padding after - * a second. We have two animations so that navigating to an anchor within the - * page always restarts the animation. - * - * See for more details. + * top. */ :target::before { content: ""; @@ -53,25 +47,6 @@ ul.nav .main { margin-top: -75px; height: 75px; pointer-events: none; - animation: 0s 1s forwards collapse-anchor-padding-1; -} - -.clicky :target::before { - animation-name: collapse-anchor-padding-2; -} - -@keyframes collapse-anchor-padding-1 { - to { - margin-top: 0; - height: 0; - } -} - -@keyframes collapse-anchor-padding-2 { - to { - margin-top: 0; - height: 0; - } } h1 { diff --git a/mkdocs/themes/mkdocs/js/base.js b/mkdocs/themes/mkdocs/js/base.js index 0001d7f58d..0fac4bc7a2 100644 --- a/mkdocs/themes/mkdocs/js/base.js +++ b/mkdocs/themes/mkdocs/js/base.js @@ -78,12 +78,7 @@ $(document).ready(function() { $('body').scrollspy({ target: '.bs-sidebar', -}); - -/* Toggle the `clicky` class on the body when clicking links to let us - retrigger CSS animations. See ../css/base.css for more details. */ -$('a').click(function(e) { - $('body').toggleClass('clicky'); + offset: 100 }); /* Prevent disabled links from causing a page reload */ From 62654167833391e84cc0520840789566f403653d Mon Sep 17 00:00:00 2001 From: Yeray Diaz Diaz Date: Sun, 21 Jan 2018 21:02:51 +0000 Subject: [PATCH 011/342] Workaround Safari bug when zooming to < 100% (#1389) --- mkdocs/themes/mkdocs/css/base.css | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mkdocs/themes/mkdocs/css/base.css b/mkdocs/themes/mkdocs/css/base.css index 34de0b674a..a76addf056 100644 --- a/mkdocs/themes/mkdocs/css/base.css +++ b/mkdocs/themes/mkdocs/css/base.css @@ -181,6 +181,13 @@ footer { /* Show and affix the side nav when space allows it */ @media (min-width: 992px) { + /* Workaround a Safari bug when zooming to < 100% + https://github.com/mkdocs/mkdocs/issues/1050 */ + .col-md-9 { + box-sizing: border-box; /* csslint allow: box-sizing */ + padding-left: 25%; + width: 100%; + } .bs-sidebar .nav > .active > ul { display: block; } From 4b8687eee736429cb502e1f6a5fac005ae7b2d3a Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Tue, 23 Jan 2018 16:29:14 -0500 Subject: [PATCH 012/342] Refactor writing-your-docs.md (#1392) Present the sections in a logical manner: 1. File Layout (where to save documents) * Configure Navigation... 2. Writing with Markdown * Supported extensions... Added documentation for all included extensions. Also added some basic information about Markdown; pointing to the implementation and rules. Fixes #1385, fixes #316, and fixes #1172. --- docs/user-guide/writing-your-docs.md | 347 ++++++++++++++++++++------- mdl_ruleset.rb | 3 + 2 files changed, 268 insertions(+), 82 deletions(-) diff --git a/docs/user-guide/writing-your-docs.md b/docs/user-guide/writing-your-docs.md index ec8390c8a8..4514451fbc 100644 --- a/docs/user-guide/writing-your-docs.md +++ b/docs/user-guide/writing-your-docs.md @@ -1,19 +1,80 @@ # Writing your docs -How to write and layout your markdown source files. +How to layout and write your Markdown source files. --- -## Configure Pages and Navigation +## File layout + +Your documentation source should be written as regular Markdown files (see +[Writing with Markdown](#writing-with-markdown) below), and placed in the +[documentation directory](configuration.md#docs_dir). By default, this directory +will be named `docs` and will exist at the top level of your project, alongside +the `mkdocs.yml` configuration file. + +The simplest project you can create will look something like this: + +```no-highlight +mkdocs.yml +docs/ + index.md +``` + +By convention your project homepage should always be named `index`. Any of the +following extensions may be used for your Markdown source files: `markdown`, +`mdown`, `mkdn`, `mkd`, `md`. -The [pages configuration](/user-guide/configuration.md#pages) in your -`mkdocs.yml` defines which pages are built by MkDocs and how they appear in the -documentation navigation. If not provided, the pages configuration will be -automatically created by discovering all the Markdown files in the -[documentation directory](/user-guide/configuration.md#docs_dir). An -automatically created pages configuration will always be sorted -alphanumerically by file name. You will need to manually define your pages -configuration if you would like your pages sorted differently. +You can also create multi-page documentation, by creating several Markdown +files: + +```no-highlight +mkdocs.yml +docs/ + index.md + about.md + license.md +``` + +The file layout you use determines the URLs that are used for the generated +pages. Given the above layout, pages would be generated for the following URLs: + +```no-highlight +/ +/about/ +/license/ +``` + +You can also include your Markdown files in nested directories if that better +suits your documentation layout. + +```no-highlight +docs/ + index.md + user-guide/getting-started.md + user-guide/configuration-options.md + license.md +``` + +Source files inside nested directories will cause pages to be generated with +nested URLs, like so: + +```no-highlight +/ +/user-guide/getting-started/ +/user-guide/configuration-options/ +/license/ +``` + +### Configure Pages and Navigation + +The [pages configuration](configuration.md#pages) in your `mkdocs.yml` defines +which pages are built by MkDocs and how they appear in the documentation +navigation. If not provided, the pages configuration will be automatically +created by discovering all the Markdown files in the [documentation +directory](configuration.md#docs_dir). An automatically created pages +configuration will always be sorted alphanumerically by file name. You will need +to manually define your pages configuration if you would like your pages sorted +differently. A simple pages configuration looks like this: @@ -35,8 +96,6 @@ pages: - About: 'about.md' ``` -### Multilevel documentation - Subsections can be created by listing related pages together under a section title. For example: @@ -56,107 +115,143 @@ and About. Then under User Guide we have two pages, Writing your docs and Styling your docs. Under the About section we also have two pages, License and Release Notes. -## File layout +Note that a section cannot have a page assigned to it. Sections are only +containers for child pages and sub-sections. You may nest sections as deeply as +you like. However, be careful that you don't make it too difficult for your +users to navigate through the site navigation by over-complicating the nesting. +While sections may mirror your directly structure, they do not have to. -Your documentation source should be written as regular Markdown files, and -placed in a directory somewhere in your project. Normally this directory will be -named `docs` and will exist at the top level of your project, alongside the -`mkdocs.yml` configuration file. +## Writing with Markdown -The simplest project you can create will look something like this: +MkDocs pages must be authored in [Markdown][md], a lightweight markup language +which results in easy-to-read, easy-to-write plain text documents that can be +converted to valid HTML documents in a predictable manner. -```no-highlight -mkdocs.yml -docs/ - index.md -``` +MkDocs uses the [Python-Markdown] library to render Markdown documents to HTML. +Python-Markdown is almost completely compliant with the [reference +implementation][md], although there are a few very minor [differences]. -By convention your project homepage should always be named `index`. Any of the -following extensions may be used for your Markdown source files: `markdown`, -`mdown`, `mkdn`, `mkd`, `md`. +In addition to the base Markdown [syntax] which is common across all Markdown +implementations, MkDocs includes support for extending the Markdown syntax with +Python-Markdown [extensions]. See the MkDocs' [markdown_extensions] +configuration setting for details on how to enable extensions. -You can also create multi-page documentation, by creating several markdown -files: +MkDocs includes some extensions by default, which are highlighted below. -```no-highlight -mkdocs.yml -docs/ - index.md - about.md - license.md -``` +[Python-Markdown]: https://python-markdown.github.io/ +[md]: http://daringfireball.net/projects/markdown/ +[differences]: https://python-markdown.github.io/#differences +[syntax]: https://daringfireball.net/projects/markdown/syntax +[extensions]: https://python-markdown.github.io/extensions/ +[markdown_extensions]: configuration.md#markdown_extensions -The file layout you use determines the URLs that are used for the generated -pages. Given the above layout, pages would be generated for the following URLs: +### Internal links + +MkDocs allows you to interlink your documentation by using regular Markdown +[links]. However, there are a few additional benefits to formatting those links +specifically for MkDocs as outlines below. + +[links]: https://daringfireball.net/projects/markdown/syntax#link + +#### Linking to pages + +When linking between pages in the documentation you can simply use the regular +Markdown [linking][links] syntax, including the *relative path* to the Markdown +document you wish to link to. ```no-highlight -/ -/about/ -/license/ +Please see the [project license](license.md) for further details. ``` -You can also include your Markdown files in nested directories if that better -suits your documentation layout. +When the MkDocs build runs, these Markdown links will automatically be +transformed into an HTML hyperlink to the appropriate HTML page. + +If the target documentation file is in another directory you'll need to make +sure to include any relative directory path in the link. ```no-highlight -docs/ - index.md - user-guide/getting-started.md - user-guide/configuration-options.md - license.md +Please see the [project license](../about/license.md) for further details. ``` -Source files inside nested directories will cause pages to be generated with -nested URLs, like so: +The [toc] extension is used by MkDocs to generate an ID for every header in your +Markdown documents. You can use that ID to link to a section within a target +document by using an anchor link. The generated HTML will correctly transform +the path portion of the link, and leave the anchor portion intact. ```no-highlight -/ -/user-guide/getting-started/ -/user-guide/configuration-options/ -/license/ +Please see the [project license](about.md#license) for further details. ``` -## Linking documents +Note that IDs are created from the text of a header. All text is converted to +lowercase and any disallowed characters, including white-space, are converted to +dashes. Consecutive dashes are then reduced to a single dash. -MkDocs allows you to interlink your documentation by using regular Markdown -hyperlinks. +There are a few configuration settings provided by the toc extension which you +can set in your `mkdocs.yml` configuration file to alter the default behavior: -### Internal hyperlinks +`permalink`: -When linking between pages in the documentation you can simply use the regular -Markdown hyperlinking syntax, including the relative path to the Markdown -document you wish to link to. +: Generate permanent links at the end of each header. Default: `False`. - Please see the [project license](license.md) for further details. + When set to True the paragraph symbol (¶ or `¶`) is used as the + link text. When set to a string, the provided string is used as the link + text. For example, to use the hash symbol (`#`) instead, do: -When the MkDocs build runs, these hyperlinks will automatically be transformed -into a hyperlink to the appropriate HTML page. + markdown_extensions: + - toc: + permalink: "#" -When working on your documentation you should be able to open the linked -Markdown document in a new editor window simply by clicking on the link. +`baselevel`: -If the target documentation file is in another directory you'll need to make -sure to include any relative directory path in the hyperlink. +: Base level for headers. Default: `1`. + + This setting allows the header levels to be automatically adjusted to fit + within the hierarchy of your HTML templates. For example, if the Markdown + text for a page should not contain any headers higher than level 2 (`

`), + do: + + markdown_extensions: + - toc: + baselevel: 2 + + Then any headers in your document would be increased by 1. For example, the + header `# Header` would be rendered as a level 2 header (`

`) in the HTML + output. + +`separator`: - Please see the [project license](../about/license.md) for further details. +: Word separator. Default: `-`. -You can also link to a section within a target documentation page by using an -anchor link. The generated HTML will correctly transform the path portion of the -hyperlink, and leave the anchor portion intact. + Character which replaces white-space in generated IDs. If you prefer + underscores, then do: + + markdown_extensions: + - toc: + separator: "_" + +Note that if you would like to define multiple of the above settings, you must +do so under a single `toc` entry in the `markdown_extensions` configuration +option. + +```yml +markdown_extensions: + - toc: + permalink: "#" + baselevel: 2 + separator: "_" +``` - Please see the [project license](about.md#license) for further details. +[toc]: https://python-markdown.github.io/extensions/toc/ -## Images and media +#### Linking to images and media As well as the Markdown source files, you can also include other file types in your documentation, which will be copied across when generating your documentation site. These might include images and other media. For example, if your project documentation needed to include a [GitHub pages -CNAME -file](https://help.github.com/articles/using-a-custom-domain-with-github-pages/) -and a PNG formatted screenshot image then your file layout might look as -follows: +CNAME file] and a PNG formatted screenshot image then your file layout might +look as follows: ```no-highlight mkdocs.yml @@ -180,15 +275,89 @@ Cupcake indexer is a snazzy new project for indexing small cakes. *Above: Cupcake indexer in progress* ``` -You image will now be embedded when you build the documentation, and should also -be previewed if you're working on the documentation with a Markdown editor. +Your image will now be embedded when you build the documentation, and should +also be previewed if you're working on the documentation with a Markdown editor. + +[GitHub pages CNAME file]: https://help.github.com/articles/using-a-custom-domain-with-github-pages/ + +### Meta-Data + +MkDocs includes support for [MultiMarkdown] style meta-data (often called +front-matter). Meta-data consists of a series of keywords and values defined at +the beginning of a Markdown document like this: + +```no-highlight +Title: My Document +Summary: A brief description of my document. +Authors: Waylan Limberg + Tom Christie +Date: January 23, 2018 +blank-value: +some_url: http://example.com + +This is the first paragraph of the document. +``` + +The keywords are case-insensitive and may consist of letters, numbers, +underscores and dashes and must end with a colon. The values consist of anything +following the colon on the line and may even be blank. + +If a line is indented by 4 or more spaces, that line is assumed to be an +additional line of the value for the previous keyword. A keyword may have as +many lines as desired. -## Markdown extensions +The first blank line ends all meta-data for the document. Therefore, the first +line of a document must not be blank. -MkDocs supports the following Markdown extensions. +Alternatively, you may use YAML style deliminators to mark the start and/or end +of your meta-data. When doing so, the first line of your document must be `---`. +The meta-data ends at the first blank line or the first line containing an end +deliminator (either `---` or `...`), whichever comes first. Even though YAML +deliminators are supported, meta-data is not parsed as YAML. + +All meta-data is stripped from the document prior to being processing by +Python-Markdown. The keys and values are passed by MkDocs to the page template. +Therefore, if a theme includes support, the values of any keys can be displayed +on the page. See the documentation for your theme for information about which +keys may be supported, if any. + +In addition to displaying information in a template, MkDocs includes support for +a few predefined meta-data keys which can alter the behavior of MkDocs for that +specific page. The following keys are supported: + +`template`: + +: The template to use with the current page. + + By default, MkDocs uses the `main.html` template of a theme to render + Markdown pages. You can use the `template` meta-data key to define a + different template file for that specific page. The template file must be + available on the path(s) defined in the theme's environment. + +`title`: + +: The "title" to use for the document. + + MkDocs will attempt to determine the title of a document in the following + ways, in order: + + 1. A title defined in the [pages] configuration setting for a document. + 2. A title defined in the `title` meta-data key of a document. + 3. A level 1 Markdown header on the first line of the document body. + 4. The filename of a document. + + Upon finding a title for a page, MkDoc does not continue checking any + additional sources in the above list. + +[MultiMarkdown]: http://fletcherpenney.net/MultiMarkdown_Syntax_Guide#metadata +[pages]: configuration.md#pages ### Tables +The [tables] extension adds a basic table syntax to Markdown which is popular +across multiple implementations. The syntax is rather simple and is generally +only useful for simple tabular data. + A simple table looks like this: ```no-highlight @@ -216,8 +385,17 @@ Left | Center | Right Left | Center | Right ``` +Note that table cells cannot contain any block level elements and cannot contain +multiple lines of text. They can, however, include inline Markdown as defined in +Markdown's [syntax] rules. + +[tables]: https://python-markdown.github.io/extensions/tables/ + ### Fenced code blocks +The [fenced code blocks] extension adds an alternate method of defining code +blocks without indentation. + The first line should contain 3 or more backtick (`` ` ``) characters, and the last line should contain the same number of backtick characters (`` ` ``): @@ -232,7 +410,7 @@ code block. ~~~ With this approach, the language can optionally be specified on the first line -after the backticks: +after the backticks which informs any syntax highlighters of the language used: ~~~no-highlight ```python @@ -240,3 +418,8 @@ def fn(): pass ``` ~~~ + +Note that fenced code blocks can not be indented. Therefore, they cannot be +nested inside list items, blockquotes, etc. + +[fenced code blocks]: https://python-markdown.github.io/extensions/fenced_code_blocks/ diff --git a/mdl_ruleset.rb b/mdl_ruleset.rb index e0312822f7..7016b57a43 100644 --- a/mdl_ruleset.rb +++ b/mdl_ruleset.rb @@ -2,3 +2,6 @@ # Disable line length check for tables and code blocks rule 'MD013', :line_length => 80, :code_blocks => false, :tables => false + +# Set Ordered list item prefix to "ordered" (use 1. 2. 3. not 1. 1. 1.) +rule 'MD029', :style => "ordered" From 462584f32842fb70847fe6764d7d4ba54509c3e6 Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Thu, 25 Jan 2018 13:07:15 -0500 Subject: [PATCH 013/342] Provide more version info. (#1393) This should help when providing support to users with multiple Pythons installed on their system. The `--version` flag's output mimics pip. Specifically, the location `mkdocs` is installed at and the Python version it is installed under. Typical output might look like: mkdocs, version 0.17.2 from /usr/local/lib/python2.7/site-packages/mkdocs (Python 2.7) --- mkdocs/__main__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mkdocs/__main__.py b/mkdocs/__main__.py index 8a0ce35407..95c649f804 100644 --- a/mkdocs/__main__.py +++ b/mkdocs/__main__.py @@ -2,6 +2,8 @@ # coding: utf-8 from __future__ import unicode_literals +import os +import sys import logging import click import socket @@ -88,9 +90,13 @@ def common_options(f): "overrides the value specified in config") force_help = "Force the push to the repository." +pgk_dir = os.path.dirname(os.path.abspath(__file__)) + @click.group(context_settings={'help_option_names': ['-h', '--help']}) -@click.version_option(__version__, '-V', '--version') +@click.version_option( + '{0} from {1} (Python {2})'.format(__version__, pgk_dir, sys.version[:3]), + '-V', '--version') @common_options def cli(): """ From 8a6d7f624fc4ee5102789b3afd5df50453364adf Mon Sep 17 00:00:00 2001 From: Yeray Diaz Diaz Date: Sat, 27 Jan 2018 21:05:29 +0000 Subject: [PATCH 014/342] Avoid `background-attachment: fixed` for better performance (#1395) Fixes #1394. --- mkdocs/themes/mkdocs/css/base.css | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/mkdocs/themes/mkdocs/css/base.css b/mkdocs/themes/mkdocs/css/base.css index a76addf056..2ff5fe372f 100644 --- a/mkdocs/themes/mkdocs/css/base.css +++ b/mkdocs/themes/mkdocs/css/base.css @@ -1,8 +1,20 @@ body { padding-top: 70px; - background: url(../img/grid.png) repeat-x; - background-attachment: fixed; +} + +/* Replacement for `body { background-attachment: fixed; }`, which has + performance issues when scrolling on large displays. See #1394. */ +body::before { + content: ' '; + position: fixed; + width: 100%; + height: 100%; + top: 0; + left: 0; background-color: #f8f8f8; + background: url(../img/grid.png) repeat-x; + will-change: transform; + z-index: -1; } body > .container { From cc03d7666e03cb3a30d991b5936c534b85772653 Mon Sep 17 00:00:00 2001 From: Christian Oliff Date: Mon, 29 Jan 2018 23:07:59 +0900 Subject: [PATCH 015/342] Always load Google Analytics over HTTPS (#1397) Is faster, safer and the recommended method by Google. --- mkdocs/themes/mkdocs/base.html | 2 +- mkdocs/themes/readthedocs/base.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mkdocs/themes/mkdocs/base.html b/mkdocs/themes/mkdocs/base.html index 115d0c7aa8..0dbb3e4474 100644 --- a/mkdocs/themes/mkdocs/base.html +++ b/mkdocs/themes/mkdocs/base.html @@ -44,7 +44,7 @@ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); ga('create', '{{ config.google_analytics[0] }}', '{{ config.google_analytics[1] }}'); ga('send', 'pageview'); diff --git a/mkdocs/themes/readthedocs/base.html b/mkdocs/themes/readthedocs/base.html index ccca048e3d..442b32bc5c 100644 --- a/mkdocs/themes/readthedocs/base.html +++ b/mkdocs/themes/readthedocs/base.html @@ -49,7 +49,7 @@ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); ga('create', '{{ config.google_analytics[0] }}', '{{ config.google_analytics[1] }}'); ga('send', 'pageview'); From d9ced99099c5f69fc8060eed92626b1cfaaa4a3d Mon Sep 17 00:00:00 2001 From: Christian Oliff Date: Tue, 30 Jan 2018 01:41:46 +0900 Subject: [PATCH 016/342] Update RespondJS to latest version (#1398) Polyfill for media queries. REF: https://github.com/scottjehl/Respond/releases --- mkdocs/themes/mkdocs/base.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs/themes/mkdocs/base.html b/mkdocs/themes/mkdocs/base.html index 0dbb3e4474..17016c7e63 100644 --- a/mkdocs/themes/mkdocs/base.html +++ b/mkdocs/themes/mkdocs/base.html @@ -30,7 +30,7 @@ From 00d0ab9228230873091ded85cb0ee4d3453c719d Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Tue, 16 Jan 2018 21:39:51 +0100 Subject: [PATCH 017/342] Add a title attribute to the search input --- mkdocs/themes/mkdocs/search-modal.html | 4 ++-- mkdocs/themes/readthedocs/search.html | 2 +- mkdocs/themes/readthedocs/searchbox.html | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mkdocs/themes/mkdocs/search-modal.html b/mkdocs/themes/mkdocs/search-modal.html index 4ab12c507a..ec64928e84 100644 --- a/mkdocs/themes/mkdocs/search-modal.html +++ b/mkdocs/themes/mkdocs/search-modal.html @@ -12,7 +12,7 @@

- +
@@ -21,4 +21,4 @@ - \ No newline at end of file + diff --git a/mkdocs/themes/readthedocs/search.html b/mkdocs/themes/readthedocs/search.html index 19a7093eeb..83763d2169 100644 --- a/mkdocs/themes/readthedocs/search.html +++ b/mkdocs/themes/readthedocs/search.html @@ -6,7 +6,7 @@

Search Results

diff --git a/mkdocs/themes/readthedocs/searchbox.html b/mkdocs/themes/readthedocs/searchbox.html index 177fcb391f..2a603ddec1 100644 --- a/mkdocs/themes/readthedocs/searchbox.html +++ b/mkdocs/themes/readthedocs/searchbox.html @@ -1,5 +1,5 @@
- +
From 84f9d16b4fd9ce0861029fc405b6aacb03a429ed Mon Sep 17 00:00:00 2001 From: Pierre Rudloff Date: Fri, 2 Feb 2018 21:58:34 +0100 Subject: [PATCH 018/342] Defer scripts (#1380) Currently, page rendering is delayed until all scripts are loaded. But these scripts are not essential to the main content. The `defer` attribute allows the browser to render the page before the scripts are completely loaded so the user can see the webpage earlier. --- mkdocs/themes/mkdocs/base.html | 10 +++++----- mkdocs/themes/readthedocs/base.html | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/mkdocs/themes/mkdocs/base.html b/mkdocs/themes/mkdocs/base.html index 17016c7e63..ebe7c1939d 100644 --- a/mkdocs/themes/mkdocs/base.html +++ b/mkdocs/themes/mkdocs/base.html @@ -33,9 +33,9 @@ - - - + + + {%- endblock %} {%- block analytics %} @@ -78,9 +78,9 @@ {%- block scripts %} - + {%- for path in extra_javascript %} - + {%- endfor %} {%- endblock %} diff --git a/mkdocs/themes/readthedocs/base.html b/mkdocs/themes/readthedocs/base.html index 442b32bc5c..c80d7d9fe7 100644 --- a/mkdocs/themes/readthedocs/base.html +++ b/mkdocs/themes/readthedocs/base.html @@ -36,9 +36,9 @@ var mkdocs_page_url = {{ page.abs_url|tojson|safe }}; {% endif %} - - - + + + {%- endblock %} {%- block extrahead %} {% endblock %} @@ -121,9 +121,9 @@ {%- block scripts %} - + {%- for path in extra_javascript %} - + {%- endfor %} {%- endblock %} From a991b7a7e6a783991c0e96ebffde18b45ac23871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Hrb=C3=A1=C4=8D?= Date: Fri, 2 Feb 2018 21:59:59 +0100 Subject: [PATCH 019/342] Compressed sitemap.xml (#1130) --- mkdocs/commands/build.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mkdocs/commands/build.py b/mkdocs/commands/build.py index 826db880b1..d8a5cd27d6 100644 --- a/mkdocs/commands/build.py +++ b/mkdocs/commands/build.py @@ -6,6 +6,7 @@ import io import logging import os +import gzip from jinja2.exceptions import TemplateNotFound import jinja2 @@ -95,6 +96,11 @@ def build_template(template_name, env, config, site_navigation=None): if output_content.strip(): output_path = os.path.join(config['site_dir'], template_name) utils.write_file(output_content.encode('utf-8'), output_path) + + if template_name == 'sitemap.xml': + log.debug("Gzipping template: %s", template_name) + with gzip.open('{}.gz'.format(output_path), 'wb') as f: + f.write(output_content.encode('utf-8')) else: log.info("Template skipped: '{}'. Generated empty output.".format(template_name)) From a63d885cc837ba2d2791b3aa748619a46b62b246 Mon Sep 17 00:00:00 2001 From: Mario Date: Sat, 3 Feb 2018 05:35:21 +0100 Subject: [PATCH 020/342] Update Tables documentation (#1361) --- docs/user-guide/writing-your-docs.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/user-guide/writing-your-docs.md b/docs/user-guide/writing-your-docs.md index 4514451fbc..91fde615d3 100644 --- a/docs/user-guide/writing-your-docs.md +++ b/docs/user-guide/writing-your-docs.md @@ -389,6 +389,9 @@ Note that table cells cannot contain any block level elements and cannot contain multiple lines of text. They can, however, include inline Markdown as defined in Markdown's [syntax] rules. +Additionally, a table must be surrounded by blank lines. There must be a blank +line before and after the table. + [tables]: https://python-markdown.github.io/extensions/tables/ ### Fenced code blocks From a793eede8b069f5dd9882372813252a1aae567e7 Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Tue, 6 Feb 2018 14:58:45 -0500 Subject: [PATCH 021/342] Update release notes. (#1403) --- docs/about/release-notes.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md index 08bad39bae..be7b2f49d8 100644 --- a/docs/about/release-notes.md +++ b/docs/about/release-notes.md @@ -23,7 +23,21 @@ The current and past members of the MkDocs team. ## Development Version +* Compress `sitemap.xml` (#1130). +* Defer loading JS scripts (#1380). +* Add a title attribute to the search input (#1379). +* Update RespondJS to latest version (#1398). +* Always load Google Analytics over HTTPS (#1397). +* Improve scrolling frame rate (#1394). +* Provide more version info. (#1393). +* Refactor `writing-your-docs.md` (#1392). +* Workaround Safari bug when zooming to < 100% (#1389). +* Remove addition of `clicky` class to body and animations. (#1387). +* Prevent search plugin from reinjecting `extra_javascript` files (#1388). * Refactor `copy_media_files` util function for more flexibility (#1370). +* Remove PyPI Deployment Docs (#1360). +* Update links to Python-Markdown library (#1360). +* Document how to generate manpages for MkDocs commands (#686). ## Version 0.17.2 (2017-11-15) From 0dce2b2b9e8727938242011a9184a4be70d4f8ee Mon Sep 17 00:00:00 2001 From: Anatoli Babenia Date: Thu, 22 Feb 2018 17:31:19 +0300 Subject: [PATCH 022/342] Expand {sha} and {version} is user commit message (#1410) --- docs/about/release-notes.md | 1 + mkdocs/__main__.py | 4 ++-- mkdocs/commands/gh_deploy.py | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md index be7b2f49d8..b690b21573 100644 --- a/docs/about/release-notes.md +++ b/docs/about/release-notes.md @@ -23,6 +23,7 @@ The current and past members of the MkDocs team. ## Development Version +* Expand {sha} and {version} in gh-deploy commit message (#1410). * Compress `sitemap.xml` (#1130). * Defer loading JS scripts (#1380). * Add a title attribute to the search input (#1379). diff --git a/mkdocs/__main__.py b/mkdocs/__main__.py index 95c649f804..8c231180fe 100644 --- a/mkdocs/__main__.py +++ b/mkdocs/__main__.py @@ -82,8 +82,8 @@ def common_options(f): reload_help = "Enable the live reloading in the development server (this is the default)" no_reload_help = "Disable the live reloading in the development server." dirty_reload_help = "Enable the live reloading in the development server, but only re-build files that have changed" -commit_message_help = ("A commit message to use when commiting to the " - "Github Pages remote branch") +commit_message_help = ("A commit message to use when committing to the " + "Github Pages remote branch. Commit {sha} and MkDocs {version} are available as expansions") remote_branch_help = ("The remote branch to commit to for Github Pages. This " "overrides the value specified in config") remote_name_help = ("The remote name to commit to for Github Pages. This " diff --git a/mkdocs/commands/gh_deploy.py b/mkdocs/commands/gh_deploy.py index 13d359cff1..b7b54be713 100644 --- a/mkdocs/commands/gh_deploy.py +++ b/mkdocs/commands/gh_deploy.py @@ -56,8 +56,9 @@ def gh_deploy(config, message=None, force=False): 'repository') if message is None: - sha = _get_current_sha() - message = default_message.format(version=mkdocs.__version__, sha=sha) + message = default_message + sha = _get_current_sha() + message = message.format(version=mkdocs.__version__, sha=sha) remote_branch = config['remote_branch'] remote_name = config['remote_name'] From dd7e2d910bcc3339b51d8f9c18866d28e6a73b70 Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Mon, 5 Mar 2018 19:42:20 -0500 Subject: [PATCH 023/342] Refactor search plugin (#1418) * Use a web worker in the browser with a fallback (fixes #859 & closes #1396). * Optionally pre-build search index (fixes #859 & closes #1061). * Upgrade to lunr.js 2.x (fixes #1319). * Support search in languages other than English (fixes #826). * Allow the user to define the word separators (fixes #867). * Only run searches for queries of length > 2 (fixes #1127). * Remove dependency on require.js, mustache, etc. (fixes #1218). * Compress the search index (fixes #1128). --- .jshintignore | 5 +- docs/about/release-notes.md | 24 + docs/user-guide/configuration.md | 85 + docs/user-guide/custom-themes.md | 101 +- mkdocs/contrib/legacy_search/__init__.py | 42 - .../templates/search/lunr.min.js | 7 - .../templates/search/mustache.min.js | 1 - .../legacy_search/templates/search/require.js | 36 - .../search/search-results-template.mustache | 4 - .../legacy_search/templates/search/search.js | 92 - .../legacy_search/templates/search/text.js | 390 --- mkdocs/contrib/search/__init__.py | 87 + .../contrib/search/lunr-language/lunr.da.js | 284 ++ .../contrib/search/lunr-language/lunr.de.js | 384 +++ .../contrib/search/lunr-language/lunr.du.js | 448 +++ .../contrib/search/lunr-language/lunr.es.js | 599 ++++ .../contrib/search/lunr-language/lunr.fi.js | 541 +++ .../contrib/search/lunr-language/lunr.fr.js | 703 ++++ .../contrib/search/lunr-language/lunr.hu.js | 565 ++++ .../contrib/search/lunr-language/lunr.it.js | 617 ++++ .../contrib/search/lunr-language/lunr.jp.js | 134 + .../search/lunr-language/lunr.multi.js | 75 + .../contrib/search/lunr-language/lunr.no.js | 258 ++ .../contrib/search/lunr-language/lunr.pt.js | 570 ++++ .../contrib/search/lunr-language/lunr.ro.js | 558 +++ .../contrib/search/lunr-language/lunr.ru.js | 391 +++ .../lunr-language/lunr.stemmer.support.js | 304 ++ .../contrib/search/lunr-language/lunr.sv.js | 256 ++ .../contrib/search/lunr-language/lunr.th.js | 97 + .../contrib/search/lunr-language/lunr.tr.js | 1087 ++++++ mkdocs/contrib/search/prebuild-index.js | 53 + .../{legacy_search => search}/search_index.py | 39 +- .../contrib/search/templates/search/lunr.js | 2986 +++++++++++++++++ .../contrib/search/templates/search/main.js | 94 + .../contrib/search/templates/search/worker.js | 127 + mkdocs/tests/search_tests.py | 281 +- setup.py | 2 +- 37 files changed, 11696 insertions(+), 631 deletions(-) delete mode 100644 mkdocs/contrib/legacy_search/__init__.py delete mode 100644 mkdocs/contrib/legacy_search/templates/search/lunr.min.js delete mode 100644 mkdocs/contrib/legacy_search/templates/search/mustache.min.js delete mode 100644 mkdocs/contrib/legacy_search/templates/search/require.js delete mode 100644 mkdocs/contrib/legacy_search/templates/search/search-results-template.mustache delete mode 100644 mkdocs/contrib/legacy_search/templates/search/search.js delete mode 100644 mkdocs/contrib/legacy_search/templates/search/text.js create mode 100644 mkdocs/contrib/search/__init__.py create mode 100644 mkdocs/contrib/search/lunr-language/lunr.da.js create mode 100644 mkdocs/contrib/search/lunr-language/lunr.de.js create mode 100644 mkdocs/contrib/search/lunr-language/lunr.du.js create mode 100644 mkdocs/contrib/search/lunr-language/lunr.es.js create mode 100644 mkdocs/contrib/search/lunr-language/lunr.fi.js create mode 100644 mkdocs/contrib/search/lunr-language/lunr.fr.js create mode 100644 mkdocs/contrib/search/lunr-language/lunr.hu.js create mode 100644 mkdocs/contrib/search/lunr-language/lunr.it.js create mode 100644 mkdocs/contrib/search/lunr-language/lunr.jp.js create mode 100644 mkdocs/contrib/search/lunr-language/lunr.multi.js create mode 100644 mkdocs/contrib/search/lunr-language/lunr.no.js create mode 100644 mkdocs/contrib/search/lunr-language/lunr.pt.js create mode 100644 mkdocs/contrib/search/lunr-language/lunr.ro.js create mode 100644 mkdocs/contrib/search/lunr-language/lunr.ru.js create mode 100644 mkdocs/contrib/search/lunr-language/lunr.stemmer.support.js create mode 100644 mkdocs/contrib/search/lunr-language/lunr.sv.js create mode 100644 mkdocs/contrib/search/lunr-language/lunr.th.js create mode 100644 mkdocs/contrib/search/lunr-language/lunr.tr.js create mode 100644 mkdocs/contrib/search/prebuild-index.js rename mkdocs/contrib/{legacy_search => search}/search_index.py (79%) create mode 100644 mkdocs/contrib/search/templates/search/lunr.js create mode 100644 mkdocs/contrib/search/templates/search/main.js create mode 100644 mkdocs/contrib/search/templates/search/worker.js diff --git a/.jshintignore b/.jshintignore index 7980294a25..d296265582 100644 --- a/.jshintignore +++ b/.jshintignore @@ -2,6 +2,5 @@ mkdocs/themes/**/js/highlight.pack.js mkdocs/themes/**/js/jquery-**.min.js mkdocs/themes/**/js/bootstrap-**.min.js mkdocs/themes/**/js/modernizr-**.min.js -mkdocs/contrib/legacy_search/templates/search/require.js -mkdocs/contrib/legacy_search/templates/search/mustache.min.js -mkdocs/contrib/legacy_search/templates/search/lunr.min.js +mkdocs/contrib/search/templates/search/lunr.js +mkdocs/contrib/search/lunr-language/lunr.**.js diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md index b690b21573..d05d32f28c 100644 --- a/docs/about/release-notes.md +++ b/docs/about/release-notes.md @@ -23,6 +23,30 @@ The current and past members of the MkDocs team. ## Development Version +### Major Additions to Development Version + +#### Refactor Search Plugin + +The search plugin has been completely refactored to include support for the +following features: + +* Use a web worker in the browser with a fallback (#1396). +* Optionally pre-build search index locally (#859 & #1061). +* Upgrade to lunr.js 2.x (#1319). +* Support search in languages other than English (#826). +* Allow the user to define the word separators (#867). +* Only run searches for queries of length > 2 (#1127). +* Remove dependency on require.js (#1218). +* Compress the search index (#1128). + +Users can review the [configuration options][search config] available and theme +authors should review how [search and themes] interact. + +[search config]: ../user-guide/configuration.md#search +[search and themes]: ../user-guide/custom-themes.md#search_and_themes + +### Other Changes and Additions to Development Version + * Expand {sha} and {version} in gh-deploy commit message (#1410). * Compress `sitemap.xml` (#1130). * Defer loading JS scripts (#1380). diff --git a/docs/user-guide/configuration.md b/docs/user-guide/configuration.md index 0c04728e03..7ed48b26db 100644 --- a/docs/user-guide/configuration.md +++ b/docs/user-guide/configuration.md @@ -445,6 +445,87 @@ plugins: [] **default**: `['search']` (the "search" plugin included with MkDocs). +#### Search + +A search plugin is provided by default with MkDocs which uses [lunr.js] as a +search engine. The following config options are available to alter the behavior +of the search plugin: + +##### **separator** + +A regular expression which matches the characters used as word separators when +building the index. By default whitespace and the hyphen (`-`) are used. To add +the dot (`.`) as a word separator you might do this: + +```yaml +plugins: + - search: + separator: '[\s\-\.]+' +``` + + **default**: `'[\s\-]+'` + +##### **lang** + +A list of languages to use when building the search index as identified by their +[ISO 639-1] language codes. With [Lunr Languages], the following languages are +supported: + +* `da`: Danish +* `du`: Dutch +* `en`: English +* `fi`: Finnish +* `fr`: French +* `de`: German +* `hu`: Hungarian +* `it`: Italian +* `jp`: Japanese +* `no`: Norwegian +* `pt`: Portuguese +* `ro`: Romanian +* `ru`: Russian +* `es`: Spanish +* `sv`: Swedish +* `th`: Thai +* `tr`: Turkish + +You may [contribute additional languages]. + +!!! Warning + + While search does support using multiple languages together, it is best not + to add additional languages unless you really need them. Each additional + language adds significant bandwidth requirements and uses more browser + resources. Generally it is best to keep each instance of MkDocs to a single + language. + +!!! Note + + Lunr Languages does not currently include support for Chinese or other Asian + languages. However, some users have reported decent results using Japanese. + +**default**: `['en']` + +##### **prebuild_index** + + Optionally generates a pre-built index of all pages, which provides some + performance improvements for larger sites. Before enabling, check that the + theme you are using explicitly supports using a prebuilt index (the builtin + themes do). The pre-build script requires that [Node.js] be installed and the + command `node` be on the system path. If this feature is enabled and fails for + any reason, a warning is issued. You may use the `--strict` flag when building + to cause such a failure to raise an error instead. + + !!! Note + + On smaller sites, using a pre-built index is not recommended as it creates a + significant increase is bandwidth requirements with little to no noticeable + improvement to your users. However, for larger sites (hundreds of pages), + the bandwidth increase is relatively small and your users will notice a + significant improvement in search performance. + + **default**: `False` + [custom themes]: custom-themes.md [variables that are available]: custom-themes.md#template-variables [pymdk-extensions]: https://python-markdown.github.io/extensions/ @@ -457,3 +538,7 @@ plugins: [] [styling your docs]: styling-your-docs.md [extra_css]: #extra_css [Plugins]: plugins.md +[lunr.js]: http://lunrjs.com/ +[ISO 639-1]: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes +[Lunr Languages]: https://github.com/MihaiValentin/lunr-languages#lunr-languages----- +[contribute additional languages]: https://github.com/MihaiValentin/lunr-languages/blob/master/CONTRIBUTING.md diff --git a/docs/user-guide/custom-themes.md b/docs/user-guide/custom-themes.md index aa3aa5bc7d..2f0021dd89 100644 --- a/docs/user-guide/custom-themes.md +++ b/docs/user-guide/custom-themes.md @@ -319,12 +319,12 @@ And then displayed with this HTML in the custom theme. ## Search and themes -As of MkDocs `0.17` client side search support has been added to MkDocs via the -`search` plugin. A theme needs to provide a few things for the plugin to work -with the theme. +As of MkDocs version *0.17* client side search support has been added to MkDocs +via the `search` plugin. A theme needs to provide a few things for the plugin to +work with the theme. While the `search` plugin is activated by default, users can disable the plugin -and themes should acount for this. It is recomended that theme templates wrap +and themes should account for this. It is recommended that theme templates wrap search specific markup with a check for the plugin: ```django @@ -339,8 +339,36 @@ The theme would need to implement its own search functionality client-side. However, with a few settings and the necessary templates, the plugin can provide a complete functioning client-side search tool based on [lunr.js]. -The following options can be set in the [theme's configuration file], -`mkdocs_theme.yml`: +The following HTML needs to be added to the theme so that the provided +JavaScript is able to properly load the search scripts and make relative links +to the search results from the current page. + +```django + +``` + +With properly configured settings, the following HTML in a template will add a +full search implementation to your theme. + +```django +

Search Results

+ +
+ +
+ +
+ Sorry, page not found. +
+``` + +The JavaScript in the plugin works by looking for the specific ID's used in the +above HTML. The form input for the user to type the search query must be +identified with `id="mkdocs-search-query"` and the div where the results will be +placed must be identified with `id="mkdocs-search-results"`. + +The plugin supports the following options being set in the [theme's +configuration file], `mkdocs_theme.yml`: ### include_search_page @@ -360,54 +388,47 @@ example, the `mkdocs` theme displays results on any page via a modal. Determines whether the search plugin should only generate a search index or a complete search solution. -When `search_index_only` is set to `true` or not defined, the search plugin -makes no modifications to the Jinja environment. A complete solution using the -provided index file is the responsability of the theme. - When `search_index_only` is set to `false`, then the search plugin modifies the Jinja environment by adding its own `templates` directory (with a lower precedence than the theme) and adds its scripts to the `extra_javascript` config setting. -The following HTML needs to be added to the theme so that the provided -JavaScript is able to properly load Lunr.js and make relative links to the -search results from the current page. - -```django - +When `search_index_only` is set to `true` or not defined, the search plugin +makes no modifications to the Jinja environment. A complete solution using the +provided index file is the responsibility of the theme. + +The search index is written to a JSON file at `search/search_index.json` in the +[site_dir]. The JSON object contained within the file may contain up to three +objects. + +```json +{ + config: {...}, + data: [...], + index: {...} +} ``` -!!! note - - The provided JavaScript will download the search index. For larger - documentation projects this can be a heavy operation. In those cases, it - is suggested that you either use `search_index_only: true` to only include - search on one page or load the JavaScript on an event like a form submit. - -The following HTML in a `search/search.html` template will add a full search -implementation to your theme. - -```django -

Search Results

+If present, the `config` object contains the key/value pairs of config options +defined for the plugin in the user's `mkdocs.yml` config file under +`plugings.search`. The `config` object was new in MkDocs version *1.0*. -
- -
+The `data` object contains a list of document objects. Each document object is +made up of a `location` (URL), a `title`, and `text` which can be used to create +a search index and/or display search results. -
- Sorry, page not found. -
-``` - -The JavaScript in the plugin works by looking for the specific ID's used in the -above HTML. The input for the user to type the search query must have the ID -`mkdocs-search-query` and `mkdocs-search-results` is the div where the results -will be placed. +If present, the `index` object contains a pre-built index which offers +performance improvements for larger sites. Note that the pre-built index is only +created if the user explicitly enables the [prebuild_index] config option. +Themes should expect the index to not be present, but can choose to use the +index when it is available. The `index` object was new in MkDocs version *1.0*. [Jinja2 template]: http://jinja.pocoo.org/docs/dev/ [built-in themes]: https://github.com/mkdocs/mkdocs/tree/master/mkdocs/themes [theme's configuration file]: #theme-configuration [lunr.js]: http://lunrjs.com/ +[site_dir]: configuration.md#site_dir +[prebuild_index]: configuration.md#prebuild_index ## Packaging Themes diff --git a/mkdocs/contrib/legacy_search/__init__.py b/mkdocs/contrib/legacy_search/__init__.py deleted file mode 100644 index ccb3b9dca0..0000000000 --- a/mkdocs/contrib/legacy_search/__init__.py +++ /dev/null @@ -1,42 +0,0 @@ -# coding: utf-8 - -from __future__ import absolute_import, unicode_literals - -import os -import logging -from mkdocs import utils -from mkdocs.plugins import BasePlugin -from mkdocs.contrib.legacy_search.search_index import SearchIndex - - -log = logging.getLogger(__name__) - - -class SearchPlugin(BasePlugin): - """ Add a search feature to MkDocs. """ - - def on_config(self, config, **kwargs): - "Add plugin templates and scripts to config." - if 'include_search_page' in config['theme'] and config['theme']['include_search_page']: - config['theme'].static_templates.add('search.html') - if not ('search_index_only' in config['theme'] and config['theme']['search_index_only']): - path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates') - config['theme'].dirs.append(path) - for extra_js in ('search/require.js', 'search/search.js'): - if extra_js not in config['extra_javascript']: - config['extra_javascript'].append(extra_js) - return config - - def on_pre_build(self, config, **kwargs): - "Create search index instance for later use." - self.search_index = SearchIndex() - - def on_page_context(self, context, **kwargs): - "Add page to search index." - self.search_index.add_entry_from_context(context['page']) - - def on_post_build(self, config, **kwargs): - "Build search index." - search_index = self.search_index.generate_search_index() - json_output_path = os.path.join(config['site_dir'], 'search', 'search_index.json') - utils.write_file(search_index.encode('utf-8'), json_output_path) diff --git a/mkdocs/contrib/legacy_search/templates/search/lunr.min.js b/mkdocs/contrib/legacy_search/templates/search/lunr.min.js deleted file mode 100644 index b0198dff91..0000000000 --- a/mkdocs/contrib/legacy_search/templates/search/lunr.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 0.7.0 - * Copyright (C) 2016 Oliver Nightingale - * MIT Licensed - * @license - */ -!function(){var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.7.0",t.utils={},t.utils.warn=function(t){return function(e){t.console&&console.warn&&console.warn(e)}}(this),t.utils.asString=function(t){return void 0===t||null===t?"":t.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var t=Array.prototype.slice.call(arguments),e=t.pop(),n=t;if("function"!=typeof e)throw new TypeError("last argument must be a function");n.forEach(function(t){this.hasHandler(t)||(this.events[t]=[]),this.events[t].push(e)},this)},t.EventEmitter.prototype.removeListener=function(t,e){if(this.hasHandler(t)){var n=this.events[t].indexOf(e);this.events[t].splice(n,1),this.events[t].length||delete this.events[t]}},t.EventEmitter.prototype.emit=function(t){if(this.hasHandler(t)){var e=Array.prototype.slice.call(arguments,1);this.events[t].forEach(function(t){t.apply(void 0,e)})}},t.EventEmitter.prototype.hasHandler=function(t){return t in this.events},t.tokenizer=function(e){return arguments.length&&null!=e&&void 0!=e?Array.isArray(e)?e.map(function(e){return t.utils.asString(e).toLowerCase()}):e.toString().trim().toLowerCase().split(t.tokenizer.seperator):[]},t.tokenizer.seperator=/[\s\-]+/,t.tokenizer.load=function(t){var e=this.registeredFunctions[t];if(!e)throw new Error("Cannot load un-registered function: "+t);return e},t.tokenizer.label="default",t.tokenizer.registeredFunctions={"default":t.tokenizer},t.tokenizer.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing tokenizer: "+n),e.label=n,this.registeredFunctions[n]=e},t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||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 n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.registeredFunctions[e];if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._stack.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");this._stack.splice(i,0,n)},t.Pipeline.prototype.remove=function(t){var e=this._stack.indexOf(t);-1!=e&&this._stack.splice(e,1)},t.Pipeline.prototype.run=function(t){for(var e=[],n=t.length,i=this._stack.length,r=0;n>r;r++){for(var o=t[r],s=0;i>s&&(o=this._stack[s](o,r,t),void 0!==o&&""!==o);s++);void 0!==o&&""!==o&&e.push(o)}return e},t.Pipeline.prototype.reset=function(){this._stack=[]},t.Pipeline.prototype.toJSON=function(){return this._stack.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Vector=function(){this._magnitude=null,this.list=void 0,this.length=0},t.Vector.Node=function(t,e,n){this.idx=t,this.val=e,this.next=n},t.Vector.prototype.insert=function(e,n){this._magnitude=void 0;var i=this.list;if(!i)return this.list=new t.Vector.Node(e,n,i),this.length++;if(en.idx?n=n.next:(i+=e.val*n.val,e=e.next,n=n.next);return i},t.Vector.prototype.similarity=function(t){return this.dot(t)/(this.magnitude()*t.magnitude())},t.SortedSet=function(){this.length=0,this.elements=[]},t.SortedSet.load=function(t){var e=new this;return e.elements=t,e.length=t.length,e},t.SortedSet.prototype.add=function(){var t,e;for(t=0;t1;){if(o===t)return r;t>o&&(e=r),o>t&&(n=r),i=n-e,r=e+Math.floor(i/2),o=this.elements[r]}return o===t?r:-1},t.SortedSet.prototype.locationFor=function(t){for(var e=0,n=this.elements.length,i=n-e,r=e+Math.floor(i/2),o=this.elements[r];i>1;)t>o&&(e=r),o>t&&(n=r),i=n-e,r=e+Math.floor(i/2),o=this.elements[r];return o>t?r:t>o?r+1:void 0},t.SortedSet.prototype.intersect=function(e){for(var n=new t.SortedSet,i=0,r=0,o=this.length,s=e.length,a=this.elements,h=e.elements;;){if(i>o-1||r>s-1)break;a[i]!==h[r]?a[i]h[r]&&r++:(n.add(a[i]),i++,r++)}return n},t.SortedSet.prototype.clone=function(){var e=new t.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},t.SortedSet.prototype.union=function(t){var e,n,i;this.length>=t.length?(e=this,n=t):(e=t,n=this),i=e.clone();for(var r=0,o=n.toArray();rp;p++)c[p]===a&&d++;h+=d/f*l.boost}}this.tokenStore.add(a,{ref:o,tf:h})}n&&this.eventEmitter.emit("add",e,this)},t.Index.prototype.remove=function(t,e){var n=t[this._ref],e=void 0===e?!0:e;if(this.documentStore.has(n)){var i=this.documentStore.get(n);this.documentStore.remove(n),i.forEach(function(t){this.tokenStore.remove(t,n)},this),e&&this.eventEmitter.emit("remove",t,this)}},t.Index.prototype.update=function(t,e){var e=void 0===e?!0:e;this.remove(t,!1),this.add(t,!1),e&&this.eventEmitter.emit("update",t,this)},t.Index.prototype.idf=function(t){var e="@"+t;if(Object.prototype.hasOwnProperty.call(this._idfCache,e))return this._idfCache[e];var n=this.tokenStore.count(t),i=1;return n>0&&(i=1+Math.log(this.documentStore.length/n)),this._idfCache[e]=i},t.Index.prototype.search=function(e){var n=this.pipeline.run(this.tokenizerFn(e)),i=new t.Vector,r=[],o=this._fields.reduce(function(t,e){return t+e.boost},0),s=n.some(function(t){return this.tokenStore.has(t)},this);if(!s)return[];n.forEach(function(e,n,s){var a=1/s.length*this._fields.length*o,h=this,u=this.tokenStore.expand(e).reduce(function(n,r){var o=h.corpusTokens.indexOf(r),s=h.idf(r),u=1,l=new t.SortedSet;if(r!==e){var c=Math.max(3,r.length-e.length);u=1/Math.log(c)}o>-1&&i.insert(o,a*s*u);for(var f=h.tokenStore.get(r),d=Object.keys(f),p=d.length,v=0;p>v;v++)l.add(f[d[v]].ref);return n.union(l)},new t.SortedSet);r.push(u)},this);var a=r.reduce(function(t,e){return t.intersect(e)});return a.map(function(t){return{ref:t,score:i.similarity(this.documentVector(t))}},this).sort(function(t,e){return e.score-t.score})},t.Index.prototype.documentVector=function(e){for(var n=this.documentStore.get(e),i=n.length,r=new t.Vector,o=0;i>o;o++){var s=n.elements[o],a=this.tokenStore.get(s)[e].tf,h=this.idf(s);r.insert(this.corpusTokens.indexOf(s),a*h)}return r},t.Index.prototype.toJSON=function(){return{version:t.version,fields:this._fields,ref:this._ref,tokenizer:this.tokenizerFn.label,documentStore:this.documentStore.toJSON(),tokenStore:this.tokenStore.toJSON(),corpusTokens:this.corpusTokens.toJSON(),pipeline:this.pipeline.toJSON()}},t.Index.prototype.use=function(t){var e=Array.prototype.slice.call(arguments,1);e.unshift(this),t.apply(this,e)},t.Store=function(){this.store={},this.length=0},t.Store.load=function(e){var n=new this;return n.length=e.length,n.store=Object.keys(e.store).reduce(function(n,i){return n[i]=t.SortedSet.load(e.store[i]),n},{}),n},t.Store.prototype.set=function(t,e){this.has(t)||this.length++,this.store[t]=e},t.Store.prototype.get=function(t){return this.store[t]},t.Store.prototype.has=function(t){return t in this.store},t.Store.prototype.remove=function(t){this.has(t)&&(delete this.store[t],this.length--)},t.Store.prototype.toJSON=function(){return{store:this.store,length:this.length}},t.stemmer=function(){var t={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"},e={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},n="[^aeiou]",i="[aeiouy]",r=n+"[^aeiouy]*",o=i+"[aeiou]*",s="^("+r+")?"+o+r,a="^("+r+")?"+o+r+"("+o+")?$",h="^("+r+")?"+o+r+o+r,u="^("+r+")?"+i,l=new RegExp(s),c=new RegExp(h),f=new RegExp(a),d=new RegExp(u),p=/^(.+?)(ss|i)es$/,v=/^(.+?)([^s])s$/,g=/^(.+?)eed$/,m=/^(.+?)(ed|ing)$/,y=/.$/,S=/(at|bl|iz)$/,w=new RegExp("([^aeiouylsz])\\1$"),k=new RegExp("^"+r+i+"[^aeiouwxy]$"),x=/^(.+?[^aeiou])y$/,b=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,E=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,F=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,_=/^(.+?)(s|t)(ion)$/,z=/^(.+?)e$/,O=/ll$/,P=new RegExp("^"+r+i+"[^aeiouwxy]$"),T=function(n){var i,r,o,s,a,h,u;if(n.length<3)return n;if(o=n.substr(0,1),"y"==o&&(n=o.toUpperCase()+n.substr(1)),s=p,a=v,s.test(n)?n=n.replace(s,"$1$2"):a.test(n)&&(n=n.replace(a,"$1$2")),s=g,a=m,s.test(n)){var T=s.exec(n);s=l,s.test(T[1])&&(s=y,n=n.replace(s,""))}else if(a.test(n)){var T=a.exec(n);i=T[1],a=d,a.test(i)&&(n=i,a=S,h=w,u=k,a.test(n)?n+="e":h.test(n)?(s=y,n=n.replace(s,"")):u.test(n)&&(n+="e"))}if(s=x,s.test(n)){var T=s.exec(n);i=T[1],n=i+"i"}if(s=b,s.test(n)){var T=s.exec(n);i=T[1],r=T[2],s=l,s.test(i)&&(n=i+t[r])}if(s=E,s.test(n)){var T=s.exec(n);i=T[1],r=T[2],s=l,s.test(i)&&(n=i+e[r])}if(s=F,a=_,s.test(n)){var T=s.exec(n);i=T[1],s=c,s.test(i)&&(n=i)}else if(a.test(n)){var T=a.exec(n);i=T[1]+T[2],a=c,a.test(i)&&(n=i)}if(s=z,s.test(n)){var T=s.exec(n);i=T[1],s=c,a=f,h=P,(s.test(i)||a.test(i)&&!h.test(i))&&(n=i)}return s=O,a=c,s.test(n)&&a.test(n)&&(s=y,n=n.replace(s,"")),"y"==o&&(n=o.toLowerCase()+n.substr(1)),n};return T}(),t.Pipeline.registerFunction(t.stemmer,"stemmer"),t.generateStopWordFilter=function(t){var e=t.reduce(function(t,e){return t[e]=e,t},{});return function(t){return t&&e[t]!==t?t:void 0}},t.stopWordFilter=t.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"]),t.Pipeline.registerFunction(t.stopWordFilter,"stopWordFilter"),t.trimmer=function(t){return t.replace(/^\W+/,"").replace(/\W+$/,"")},t.Pipeline.registerFunction(t.trimmer,"trimmer"),t.TokenStore=function(){this.root={docs:{}},this.length=0},t.TokenStore.load=function(t){var e=new this;return e.root=t.root,e.length=t.length,e},t.TokenStore.prototype.add=function(t,e,n){var n=n||this.root,i=t.charAt(0),r=t.slice(1);return i in n||(n[i]={docs:{}}),0===r.length?(n[i].docs[e.ref]=e,void(this.length+=1)):this.add(r,e,n[i])},t.TokenStore.prototype.has=function(t){if(!t)return!1;for(var e=this.root,n=0;n":">",'"':""","'":"'","/":"/"};function escapeHtml(string){return String(string).replace(/[&<>"'\/]/g,function(s){return entityMap[s]})}var whiteRe=/\s*/;var spaceRe=/\s+/;var equalsRe=/\s*=/;var curlyRe=/\s*\}/;var tagRe=/#|\^|\/|>|\{|&|=|!/;function parseTemplate(template,tags){if(!template)return[];var sections=[];var tokens=[];var spaces=[];var hasTag=false;var nonSpace=false;function stripSpace(){if(hasTag&&!nonSpace){while(spaces.length)delete tokens[spaces.pop()]}else{spaces=[]}hasTag=false;nonSpace=false}var openingTagRe,closingTagRe,closingCurlyRe;function compileTags(tags){if(typeof tags==="string")tags=tags.split(spaceRe,2);if(!isArray(tags)||tags.length!==2)throw new Error("Invalid tags: "+tags);openingTagRe=new RegExp(escapeRegExp(tags[0])+"\\s*");closingTagRe=new RegExp("\\s*"+escapeRegExp(tags[1]));closingCurlyRe=new RegExp("\\s*"+escapeRegExp("}"+tags[1]))}compileTags(tags||mustache.tags);var scanner=new Scanner(template);var start,type,value,chr,token,openSection;while(!scanner.eos()){start=scanner.pos;value=scanner.scanUntil(openingTagRe);if(value){for(var i=0,valueLength=value.length;i0?sections[sections.length-1][4]:nestedTokens;break;default:collector.push(token)}}return nestedTokens}function Scanner(string){this.string=string;this.tail=string;this.pos=0}Scanner.prototype.eos=function(){return this.tail===""};Scanner.prototype.scan=function(re){var match=this.tail.match(re);if(!match||match.index!==0)return"";var string=match[0];this.tail=this.tail.substring(string.length);this.pos+=string.length;return string};Scanner.prototype.scanUntil=function(re){var index=this.tail.search(re),match;switch(index){case-1:match=this.tail;this.tail="";break;case 0:match="";break;default:match=this.tail.substring(0,index);this.tail=this.tail.substring(index)}this.pos+=match.length;return match};function Context(view,parentContext){this.view=view;this.cache={".":this.view};this.parent=parentContext}Context.prototype.push=function(view){return new Context(view,this)};Context.prototype.lookup=function(name){var cache=this.cache;var value;if(name in cache){value=cache[name]}else{var context=this,names,index,lookupHit=false;while(context){if(name.indexOf(".")>0){value=context.view;names=name.split(".");index=0;while(value!=null&&index")value=this._renderPartial(token,context,partials,originalTemplate);else if(symbol==="&")value=this._unescapedValue(token,context);else if(symbol==="name")value=this._escapedValue(token,context);else if(symbol==="text")value=this._rawValue(token);if(value!==undefined)buffer+=value}return buffer};Writer.prototype._renderSection=function(token,context,partials,originalTemplate){var self=this;var buffer="";var value=context.lookup(token[1]);function subRender(template){return self.render(template,context,partials)}if(!value)return;if(isArray(value)){for(var j=0,valueLength=value.length;jthis.depCount&&!this.defined){if(G(l)){if(this.events.error&&this.map.isDefine||g.onError!==ca)try{f=i.execCb(c,l,b,f)}catch(d){a=d}else f=i.execCb(c,l,b,f);this.map.isDefine&&void 0===f&&((b=this.module)?f=b.exports:this.usingExports&& -(f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=l;this.exports=f;if(this.map.isDefine&&!this.ignore&&(r[c]=f,g.onResourceLoad))g.onResourceLoad(i,this.map,this.depMaps);y(c);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a= -this.map,b=a.id,d=p(a.prefix);this.depMaps.push(d);q(d,"defined",u(this,function(f){var l,d;d=m(aa,this.map.id);var e=this.map.name,P=this.map.parentMap?this.map.parentMap.name:null,n=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(e=f.normalize(e,function(a){return c(a,P,!0)})||""),f=p(a.prefix+"!"+e,this.map.parentMap),q(f,"defined",u(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),d=m(h,f.id)){this.depMaps.push(f); -if(this.events.error)d.on("error",u(this,function(a){this.emit("error",a)}));d.enable()}}else d?(this.map.url=i.nameToUrl(d),this.load()):(l=u(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),l.error=u(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(h,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),l.fromText=u(this,function(f,c){var d=a.name,e=p(d),P=M;c&&(f=c);P&&(M=!1);s(e);t(j.config,b)&&(j.config[d]=j.config[b]);try{g.exec(f)}catch(h){return w(C("fromtexteval", -"fromText eval for "+b+" failed: "+h,h,[b]))}P&&(M=!0);this.depMaps.push(e);i.completeLoad(d);n([d],l)}),f.load(a.name,n,l,j))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){V[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,u(this,function(a,b){var c,f;if("string"===typeof a){a=p(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=m(L,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;q(a,"defined",u(this,function(a){this.defineDep(b, -a);this.check()}));this.errback?q(a,"error",u(this,this.errback)):this.events.error&&q(a,"error",u(this,function(a){this.emit("error",a)}))}c=a.id;f=h[c];!t(L,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,u(this,function(a){var b=m(h,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:j,contextName:b, -registry:h,defined:r,urlFetched:S,defQueue:A,Module:Z,makeModuleMap:p,nextTick:g.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=j.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(j[b]||(j[b]={}),U(j[b],a,!0,!0)):j[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(aa[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a); -b[c]=a}),j.shim=b);a.packages&&v(a.packages,function(a){var b,a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(j.paths[b]=a.location);j.pkgs[b]=a.name+"/"+(a.main||"main").replace(ia,"").replace(Q,"")});B(h,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=p(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ba,arguments));return b||a.exports&&da(a.exports)}},makeRequire:function(a,e){function j(c,d,m){var n, -q;e.enableBuildCallback&&(d&&G(d))&&(d.__requireJsBuild=!0);if("string"===typeof c){if(G(d))return w(C("requireargs","Invalid require call"),m);if(a&&t(L,c))return L[c](h[a.id]);if(g.get)return g.get(i,c,a,j);n=p(c,a,!1,!0);n=n.id;return!t(r,n)?w(C("notloaded",'Module name "'+n+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):r[n]}J();i.nextTick(function(){J();q=s(p(null,a));q.skipMap=e.skipMap;q.init(c,d,m,{enabled:!0});D()});return j}e=e||{};U(j,{isBrowser:z,toUrl:function(b){var d, -e=b.lastIndexOf("."),k=b.split("/")[0];if(-1!==e&&(!("."===k||".."===k)||1e.attachEvent.toString().indexOf("[native code"))&& -!Y?(M=!0,e.attachEvent("onreadystatechange",b.onScriptLoad)):(e.addEventListener("load",b.onScriptLoad,!1),e.addEventListener("error",b.onScriptError,!1)),e.src=d,J=e,D?y.insertBefore(e,D):y.appendChild(e),J=null,e;if(ea)try{importScripts(d),b.completeLoad(c)}catch(m){b.onError(C("importscripts","importScripts failed for "+c+" at "+d,m,[c]))}};z&&!q.skipDataMain&&T(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(I=b.getAttribute("data-main"))return s=I,q.baseUrl||(E=s.split("/"), -s=E.pop(),O=E.length?E.join("/")+"/":"./",q.baseUrl=O),s=s.replace(Q,""),g.jsExtRegExp.test(s)&&(s=I),q.deps=q.deps?q.deps.concat(s):[s],!0});define=function(b,c,d){var e,g;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(ka,"").replace(la,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(M){if(!(e=J))N&&"interactive"===N.readyState||T(document.getElementsByTagName("script"),function(b){if("interactive"=== -b.readyState)return N=b}),e=N;e&&(b||(b=e.getAttribute("data-requiremodule")),g=F[e.getAttribute("data-requirecontext")])}(g?g.defQueue:R).push([b,c,d])};define.amd={jQuery:!0};g.exec=function(b){return eval(b)};g(q)}})(this); diff --git a/mkdocs/contrib/legacy_search/templates/search/search-results-template.mustache b/mkdocs/contrib/legacy_search/templates/search/search-results-template.mustache deleted file mode 100644 index a8b3862f20..0000000000 --- a/mkdocs/contrib/legacy_search/templates/search/search-results-template.mustache +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/mkdocs/contrib/legacy_search/templates/search/search.js b/mkdocs/contrib/legacy_search/templates/search/search.js deleted file mode 100644 index 2283930c83..0000000000 --- a/mkdocs/contrib/legacy_search/templates/search/search.js +++ /dev/null @@ -1,92 +0,0 @@ -require.config({ - baseUrl: base_url + "/search/" -}); - -require([ - 'mustache.min', - 'lunr.min', - 'text!search-results-template.mustache', - 'text!search_index.json', -], function (Mustache, lunr, results_template, data) { - "use strict"; - - function getSearchTerm() - { - var sPageURL = window.location.search.substring(1); - var sURLVariables = sPageURL.split('&'); - for (var i = 0; i < sURLVariables.length; i++) - { - var sParameterName = sURLVariables[i].split('='); - if (sParameterName[0] == 'q') - { - return decodeURIComponent(sParameterName[1].replace(/\+/g, '%20')); - } - } - } - - var index = lunr(function () { - this.field('title', {boost: 10}); - this.field('text'); - this.ref('location'); - }); - - data = JSON.parse(data); - var documents = {}; - - for (var i=0; i < data.docs.length; i++){ - var doc = data.docs[i]; - doc.location = base_url + doc.location; - index.add(doc); - documents[doc.location] = doc; - } - - var search = function(){ - - var query = document.getElementById('mkdocs-search-query').value; - var search_results = document.getElementById("mkdocs-search-results"); - while (search_results.firstChild) { - search_results.removeChild(search_results.firstChild); - } - - if(query === ''){ - return; - } - - var results = index.search(query); - - if (results.length > 0){ - for (var i=0; i < results.length; i++){ - var result = results[i]; - doc = documents[result.ref]; - doc.base_url = base_url; - doc.summary = doc.text.substring(0, 200); - var html = Mustache.to_html(results_template, doc); - search_results.insertAdjacentHTML('beforeend', html); - } - } else { - search_results.insertAdjacentHTML('beforeend', "

No results found

"); - } - - if(jQuery){ - /* - * We currently only automatically hide bootstrap models. This - * requires jQuery to work. - */ - jQuery('#mkdocs_search_modal a').click(function(){ - jQuery('#mkdocs_search_modal').modal('hide'); - }); - } - - }; - - var search_input = document.getElementById('mkdocs-search-query'); - - var term = getSearchTerm(); - if (term){ - search_input.value = term; - search(); - } - - if (search_input){search_input.addEventListener("keyup", search);} - -}); diff --git a/mkdocs/contrib/legacy_search/templates/search/text.js b/mkdocs/contrib/legacy_search/templates/search/text.js deleted file mode 100644 index 17921b6e5e..0000000000 --- a/mkdocs/contrib/legacy_search/templates/search/text.js +++ /dev/null @@ -1,390 +0,0 @@ -/** - * @license RequireJS text 2.0.12 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/requirejs/text for details - */ -/*jslint regexp: true */ -/*global require, XMLHttpRequest, ActiveXObject, - define, window, process, Packages, - java, location, Components, FileUtils */ - -define(['module'], function (module) { - 'use strict'; - - var text, fs, Cc, Ci, xpcIsWindows, - progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], - xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, - bodyRegExp = /]*>\s*([\s\S]+)\s*<\/body>/im, - hasLocation = typeof location !== 'undefined' && location.href, - defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''), - defaultHostName = hasLocation && location.hostname, - defaultPort = hasLocation && (location.port || undefined), - buildMap = {}, - masterConfig = (module.config && module.config()) || {}; - - text = { - version: '2.0.12', - - strip: function (content) { - //Strips declarations so that external SVG and XML - //documents can be added to a document without worry. Also, if the string - //is an HTML document, only the part inside the body tag is returned. - if (content) { - content = content.replace(xmlRegExp, ""); - var matches = content.match(bodyRegExp); - if (matches) { - content = matches[1]; - } - } else { - content = ""; - } - return content; - }, - - jsEscape: function (content) { - return content.replace(/(['\\])/g, '\\$1') - .replace(/[\f]/g, "\\f") - .replace(/[\b]/g, "\\b") - .replace(/[\n]/g, "\\n") - .replace(/[\t]/g, "\\t") - .replace(/[\r]/g, "\\r") - .replace(/[\u2028]/g, "\\u2028") - .replace(/[\u2029]/g, "\\u2029"); - }, - - createXhr: masterConfig.createXhr || function () { - //Would love to dump the ActiveX crap in here. Need IE 6 to die first. - var xhr, i, progId; - if (typeof XMLHttpRequest !== "undefined") { - return new XMLHttpRequest(); - } else if (typeof ActiveXObject !== "undefined") { - for (i = 0; i < 3; i += 1) { - progId = progIds[i]; - try { - xhr = new ActiveXObject(progId); - } catch (e) {} - - if (xhr) { - progIds = [progId]; // so faster next time - break; - } - } - } - - return xhr; - }, - - /** - * Parses a resource name into its component parts. Resource names - * look like: module/name.ext!strip, where the !strip part is - * optional. - * @param {String} name the resource name - * @returns {Object} with properties "moduleName", "ext" and "strip" - * where strip is a boolean. - */ - parseName: function (name) { - var modName, ext, temp, - strip = false, - index = name.indexOf("."), - isRelative = name.indexOf('./') === 0 || - name.indexOf('../') === 0; - - if (index !== -1 && (!isRelative || index > 1)) { - modName = name.substring(0, index); - ext = name.substring(index + 1, name.length); - } else { - modName = name; - } - - temp = ext || modName; - index = temp.indexOf("!"); - if (index !== -1) { - //Pull off the strip arg. - strip = temp.substring(index + 1) === "strip"; - temp = temp.substring(0, index); - if (ext) { - ext = temp; - } else { - modName = temp; - } - } - - return { - moduleName: modName, - ext: ext, - strip: strip - }; - }, - - xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/, - - /** - * Is an URL on another domain. Only works for browser use, returns - * false in non-browser environments. Only used to know if an - * optimized .js version of a text resource should be loaded - * instead. - * @param {String} url - * @returns Boolean - */ - useXhr: function (url, protocol, hostname, port) { - var uProtocol, uHostName, uPort, - match = text.xdRegExp.exec(url); - if (!match) { - return true; - } - uProtocol = match[2]; - uHostName = match[3]; - - uHostName = uHostName.split(':'); - uPort = uHostName[1]; - uHostName = uHostName[0]; - - return (!uProtocol || uProtocol === protocol) && - (!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) && - ((!uPort && !uHostName) || uPort === port); - }, - - finishLoad: function (name, strip, content, onLoad) { - content = strip ? text.strip(content) : content; - if (masterConfig.isBuild) { - buildMap[name] = content; - } - onLoad(content); - }, - - load: function (name, req, onLoad, config) { - //Name has format: some.module.filext!strip - //The strip part is optional. - //if strip is present, then that means only get the string contents - //inside a body tag in an HTML string. For XML/SVG content it means - //removing the declarations so the content can be inserted - //into the current doc without problems. - - // Do not bother with the work if a build and text will - // not be inlined. - if (config && config.isBuild && !config.inlineText) { - onLoad(); - return; - } - - masterConfig.isBuild = config && config.isBuild; - - var parsed = text.parseName(name), - nonStripName = parsed.moduleName + - (parsed.ext ? '.' + parsed.ext : ''), - url = req.toUrl(nonStripName), - useXhr = (masterConfig.useXhr) || - text.useXhr; - - // Do not load if it is an empty: url - if (url.indexOf('empty:') === 0) { - onLoad(); - return; - } - - //Load the text. Use XHR if possible and in a browser. - if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) { - text.get(url, function (content) { - text.finishLoad(name, parsed.strip, content, onLoad); - }, function (err) { - if (onLoad.error) { - onLoad.error(err); - } - }); - } else { - //Need to fetch the resource across domains. Assume - //the resource has been optimized into a JS module. Fetch - //by the module name + extension, but do not include the - //!strip part to avoid file system issues. - req([nonStripName], function (content) { - text.finishLoad(parsed.moduleName + '.' + parsed.ext, - parsed.strip, content, onLoad); - }); - } - }, - - write: function (pluginName, moduleName, write, config) { - if (buildMap.hasOwnProperty(moduleName)) { - var content = text.jsEscape(buildMap[moduleName]); - write.asModule(pluginName + "!" + moduleName, - "define(function () { return '" + - content + - "';});\n"); - } - }, - - writeFile: function (pluginName, moduleName, req, write, config) { - var parsed = text.parseName(moduleName), - extPart = parsed.ext ? '.' + parsed.ext : '', - nonStripName = parsed.moduleName + extPart, - //Use a '.js' file name so that it indicates it is a - //script that can be loaded across domains. - fileName = req.toUrl(parsed.moduleName + extPart) + '.js'; - - //Leverage own load() method to load plugin value, but only - //write out values that do not have the strip argument, - //to avoid any potential issues with ! in file names. - text.load(nonStripName, req, function (value) { - //Use own write() method to construct full module value. - //But need to create shell that translates writeFile's - //write() to the right interface. - var textWrite = function (contents) { - return write(fileName, contents); - }; - textWrite.asModule = function (moduleName, contents) { - return write.asModule(moduleName, fileName, contents); - }; - - text.write(pluginName, nonStripName, textWrite, config); - }, config); - } - }; - - if (masterConfig.env === 'node' || (!masterConfig.env && - typeof process !== "undefined" && - process.versions && - !!process.versions.node && - !process.versions['node-webkit'])) { - //Using special require.nodeRequire, something added by r.js. - fs = require.nodeRequire('fs'); - - text.get = function (url, callback, errback) { - try { - var file = fs.readFileSync(url, 'utf8'); - //Remove BOM (Byte Mark Order) from utf8 files if it is there. - if (file.indexOf('\uFEFF') === 0) { - file = file.substring(1); - } - callback(file); - } catch (e) { - if (errback) { - errback(e); - } - } - }; - } else if (masterConfig.env === 'xhr' || (!masterConfig.env && - text.createXhr())) { - text.get = function (url, callback, errback, headers) { - var xhr = text.createXhr(), header; - xhr.open('GET', url, true); - - //Allow plugins direct access to xhr headers - if (headers) { - for (header in headers) { - if (headers.hasOwnProperty(header)) { - xhr.setRequestHeader(header.toLowerCase(), headers[header]); - } - } - } - - //Allow overrides specified in config - if (masterConfig.onXhr) { - masterConfig.onXhr(xhr, url); - } - - xhr.onreadystatechange = function (evt) { - var status, err; - //Do not explicitly handle errors, those should be - //visible via console output in the browser. - if (xhr.readyState === 4) { - status = xhr.status || 0; - if (status > 399 && status < 600) { - //An http 4xx or 5xx error. Signal an error. - err = new Error(url + ' HTTP status: ' + status); - err.xhr = xhr; - if (errback) { - errback(err); - } - } else { - callback(xhr.responseText); - } - - if (masterConfig.onXhrComplete) { - masterConfig.onXhrComplete(xhr, url); - } - } - }; - xhr.send(null); - }; - } else if (masterConfig.env === 'rhino' || (!masterConfig.env && - typeof Packages !== 'undefined' && typeof java !== 'undefined')) { - //Why Java, why is this so awkward? - text.get = function (url, callback) { - var stringBuffer, line, - encoding = "utf-8", - file = new java.io.File(url), - lineSeparator = java.lang.System.getProperty("line.separator"), - input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)), - content = ''; - try { - stringBuffer = new java.lang.StringBuffer(); - line = input.readLine(); - - // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324 - // http://www.unicode.org/faq/utf_bom.html - - // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK: - // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 - if (line && line.length() && line.charAt(0) === 0xfeff) { - // Eat the BOM, since we've already found the encoding on this file, - // and we plan to concatenating this buffer with others; the BOM should - // only appear at the top of a file. - line = line.substring(1); - } - - if (line !== null) { - stringBuffer.append(line); - } - - while ((line = input.readLine()) !== null) { - stringBuffer.append(lineSeparator); - stringBuffer.append(line); - } - //Make sure we return a JavaScript string and not a Java string. - content = String(stringBuffer.toString()); //String - } finally { - input.close(); - } - callback(content); - }; - } else if (masterConfig.env === 'xpconnect' || (!masterConfig.env && - typeof Components !== 'undefined' && Components.classes && - Components.interfaces)) { - //Avert your gaze! - Cc = Components.classes; - Ci = Components.interfaces; - Components.utils['import']('resource://gre/modules/FileUtils.jsm'); - xpcIsWindows = ('@mozilla.org/windows-registry-key;1' in Cc); - - text.get = function (url, callback) { - var inStream, convertStream, fileObj, - readData = {}; - - if (xpcIsWindows) { - url = url.replace(/\//g, '\\'); - } - - fileObj = new FileUtils.File(url); - - //XPCOM, you so crazy - try { - inStream = Cc['@mozilla.org/network/file-input-stream;1'] - .createInstance(Ci.nsIFileInputStream); - inStream.init(fileObj, 1, 0, false); - - convertStream = Cc['@mozilla.org/intl/converter-input-stream;1'] - .createInstance(Ci.nsIConverterInputStream); - convertStream.init(inStream, "utf-8", inStream.available(), - Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); - - convertStream.readString(inStream.available(), readData); - convertStream.close(); - inStream.close(); - callback(readData.value); - } catch (e) { - throw new Error((fileObj && fileObj.path || '') + ': ' + e); - } - }; - } - return text; -}); diff --git a/mkdocs/contrib/search/__init__.py b/mkdocs/contrib/search/__init__.py new file mode 100644 index 0000000000..5610c8a0b5 --- /dev/null +++ b/mkdocs/contrib/search/__init__.py @@ -0,0 +1,87 @@ +# coding: utf-8 + +from __future__ import absolute_import, unicode_literals + +import os +import logging +from mkdocs import utils +from mkdocs.plugins import BasePlugin +from mkdocs.config import config_options +from mkdocs.contrib.search.search_index import SearchIndex + + +log = logging.getLogger(__name__) +base_path = os.path.dirname(os.path.abspath(__file__)) + + +class LangOption(config_options.OptionallyRequired): + """ Validate Language(s) provided in config are known languages. """ + + def lang_file_exists(self, lang): + path = os.path.join(base_path, 'lunr-language', 'lunr.{}.js'.format(lang)) + return os.path.isfile(path) + + def run_validation(self, value): + if isinstance(value, utils.string_types): + value = [value] + elif not isinstance(value, (list, tuple)): + raise config_options.ValidationError('Expected a list of language codes.') + for lang in value: + if lang != 'en' and not self.lang_file_exists(lang): + raise config_options.ValidationError( + '"{}" is not a suported language code.'.format(lang) + ) + return value + + +class SearchPlugin(BasePlugin): + """ Add a search feature to MkDocs. """ + + config_scheme = ( + ('lang', LangOption(default=['en'])), + ('separator', config_options.Type(utils.string_types, default=r'[\s\-]+')), + ('prebuild_index', config_options.Type(bool, default=False)), + ) + + def on_config(self, config, **kwargs): + "Add plugin templates and scripts to config." + if 'include_search_page' in config['theme'] and config['theme']['include_search_page']: + config['theme'].static_templates.add('search.html') + if not ('search_index_only' in config['theme'] and config['theme']['search_index_only']): + path = os.path.join(base_path, 'templates') + config['theme'].dirs.append(path) + if 'search/main.js' not in config['extra_javascript']: + config['extra_javascript'].append('search/main.js') + return config + + def on_pre_build(self, config, **kwargs): + "Create search index instance for later use." + self.search_index = SearchIndex(**self.config) + + def on_page_context(self, context, **kwargs): + "Add page to search index." + self.search_index.add_entry_from_context(context['page']) + + def on_post_build(self, config, **kwargs): + "Build search index." + output_base_path = os.path.join(config['site_dir'], 'search') + search_index = self.search_index.generate_search_index() + json_output_path = os.path.join(output_base_path, 'search_index.json') + utils.write_file(search_index.encode('utf-8'), json_output_path) + + if not ('search_index_only' in config['theme'] and config['theme']['search_index_only']): + # Include language support files in output. Copy them directly + # so that only the needed files are included. + files = [] + if len(self.config['lang']) > 1 or 'en' not in self.config['lang']: + files.append('lunr.stemmer.support.js') + if len(self.config['lang']) > 1: + files.append('lunr.multi.js') + for lang in self.config['lang']: + if (lang != 'en'): + files.append('lunr.{}.js'.format(lang)) + + for filename in files: + from_path = os.path.join(base_path, 'lunr-language', filename) + to_path = os.path.join(output_base_path, filename) + utils.copy_file(from_path, to_path) diff --git a/mkdocs/contrib/search/lunr-language/lunr.da.js b/mkdocs/contrib/search/lunr-language/lunr.da.js new file mode 100644 index 0000000000..28b5c2d5d8 --- /dev/null +++ b/mkdocs/contrib/search/lunr-language/lunr.da.js @@ -0,0 +1,284 @@ +/*! + * Lunr languages, `Danish` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +/** + * 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 environments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + factory()(root.lunr); + } +}(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 function(lunr) { + /* throw error if lunr is not yet included */ + if ('undefined' === typeof lunr) { + throw new Error('Lunr is not present. Please include / require Lunr before this script.'); + } + + /* throw error if lunr stemmer support is not yet included */ + if ('undefined' === typeof lunr.stemmerSupport) { + throw new Error('Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.'); + } + + /* register specific locale function */ + lunr.da = function() { + this.pipeline.reset(); + this.pipeline.add( + lunr.da.trimmer, + lunr.da.stopWordFilter, + lunr.da.stemmer + ); + + // for lunr version 2 + // this is necessary so that every searched word is also stemmed before + // in lunr <= 1 this is not needed, as it is done using the normal pipeline + if (this.searchPipeline) { + this.searchPipeline.reset(); + this.searchPipeline.add(lunr.da.stemmer) + } + }; + + /* lunr trimmer function */ + lunr.da.wordCharacters = "A-Za-z\xAA\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02B8\u02E0-\u02E4\u1D00-\u1D25\u1D2C-\u1D5C\u1D62-\u1D65\u1D6B-\u1D77\u1D79-\u1DBE\u1E00-\u1EFF\u2071\u207F\u2090-\u209C\u212A\u212B\u2132\u214E\u2160-\u2188\u2C60-\u2C7F\uA722-\uA787\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA7FF\uAB30-\uAB5A\uAB5C-\uAB64\uFB00-\uFB06\uFF21-\uFF3A\uFF41-\uFF5A"; + lunr.da.trimmer = lunr.trimmerSupport.generateTrimmer(lunr.da.wordCharacters); + + lunr.Pipeline.registerFunction(lunr.da.trimmer, 'trimmer-da'); + + /* lunr stemmer function */ + lunr.da.stemmer = (function() { + /* create the wrapped stemmer object */ + var Among = lunr.stemmerSupport.Among, + SnowballProgram = lunr.stemmerSupport.SnowballProgram, + st = new function DanishStemmer() { + var a_0 = [new Among("hed", -1, 1), new Among("ethed", 0, 1), + new Among("ered", -1, 1), new Among("e", -1, 1), + new Among("erede", 3, 1), new Among("ende", 3, 1), + new Among("erende", 5, 1), new Among("ene", 3, 1), + new Among("erne", 3, 1), new Among("ere", 3, 1), + new Among("en", -1, 1), new Among("heden", 10, 1), + new Among("eren", 10, 1), new Among("er", -1, 1), + new Among("heder", 13, 1), new Among("erer", 13, 1), + new Among("s", -1, 2), new Among("heds", 16, 1), + new Among("es", 16, 1), new Among("endes", 18, 1), + new Among("erendes", 19, 1), new Among("enes", 18, 1), + new Among("ernes", 18, 1), new Among("eres", 18, 1), + new Among("ens", 16, 1), new Among("hedens", 24, 1), + new Among("erens", 24, 1), new Among("ers", 16, 1), + new Among("ets", 16, 1), new Among("erets", 28, 1), + new Among("et", -1, 1), new Among("eret", 30, 1) + ], + a_1 = [ + new Among("gd", -1, -1), new Among("dt", -1, -1), + new Among("gt", -1, -1), new Among("kt", -1, -1) + ], + a_2 = [ + new Among("ig", -1, 1), new Among("lig", 0, 1), + new Among("elig", 1, 1), new Among("els", -1, 1), + new Among("l\u00F8st", -1, 2) + ], + g_v = [17, 65, 16, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 128 + ], + g_s_ending = [239, 254, 42, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16 + ], + I_x, I_p1, S_ch, sbp = new SnowballProgram(); + this.setCurrent = function(word) { + sbp.setCurrent(word); + }; + this.getCurrent = function() { + return sbp.getCurrent(); + }; + + function r_mark_regions() { + var v_1, c = sbp.cursor + 3; + I_p1 = sbp.limit; + if (0 <= c && c <= sbp.limit) { + I_x = c; + while (true) { + v_1 = sbp.cursor; + if (sbp.in_grouping(g_v, 97, 248)) { + sbp.cursor = v_1; + break; + } + sbp.cursor = v_1; + if (v_1 >= sbp.limit) + return; + sbp.cursor++; + } + while (!sbp.out_grouping(g_v, 97, 248)) { + if (sbp.cursor >= sbp.limit) + return; + sbp.cursor++; + } + I_p1 = sbp.cursor; + if (I_p1 < I_x) + I_p1 = I_x; + } + } + + function r_main_suffix() { + var among_var, v_1; + if (sbp.cursor >= I_p1) { + v_1 = sbp.limit_backward; + sbp.limit_backward = I_p1; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_0, 32); + sbp.limit_backward = v_1; + if (among_var) { + sbp.bra = sbp.cursor; + switch (among_var) { + case 1: + sbp.slice_del(); + break; + case 2: + if (sbp.in_grouping_b(g_s_ending, 97, 229)) + sbp.slice_del(); + break; + } + } + } + } + + function r_consonant_pair() { + var v_1 = sbp.limit - sbp.cursor, + v_2; + if (sbp.cursor >= I_p1) { + v_2 = sbp.limit_backward; + sbp.limit_backward = I_p1; + sbp.ket = sbp.cursor; + if (sbp.find_among_b(a_1, 4)) { + sbp.bra = sbp.cursor; + sbp.limit_backward = v_2; + sbp.cursor = sbp.limit - v_1; + if (sbp.cursor > sbp.limit_backward) { + sbp.cursor--; + sbp.bra = sbp.cursor; + sbp.slice_del(); + } + } else + sbp.limit_backward = v_2; + } + } + + function r_other_suffix() { + var among_var, v_1 = sbp.limit - sbp.cursor, + v_2, v_3; + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(2, "st")) { + sbp.bra = sbp.cursor; + if (sbp.eq_s_b(2, "ig")) + sbp.slice_del(); + } + sbp.cursor = sbp.limit - v_1; + if (sbp.cursor >= I_p1) { + v_2 = sbp.limit_backward; + sbp.limit_backward = I_p1; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_2, 5); + sbp.limit_backward = v_2; + if (among_var) { + sbp.bra = sbp.cursor; + switch (among_var) { + case 1: + sbp.slice_del(); + v_3 = sbp.limit - sbp.cursor; + r_consonant_pair(); + sbp.cursor = sbp.limit - v_3; + break; + case 2: + sbp.slice_from("l\u00F8s"); + break; + } + } + } + } + + function r_undouble() { + var v_1; + if (sbp.cursor >= I_p1) { + v_1 = sbp.limit_backward; + sbp.limit_backward = I_p1; + sbp.ket = sbp.cursor; + if (sbp.out_grouping_b(g_v, 97, 248)) { + sbp.bra = sbp.cursor; + S_ch = sbp.slice_to(S_ch); + sbp.limit_backward = v_1; + if (sbp.eq_v_b(S_ch)) + sbp.slice_del(); + } else + sbp.limit_backward = v_1; + } + } + this.stem = function() { + var v_1 = sbp.cursor; + r_mark_regions(); + sbp.limit_backward = v_1; + sbp.cursor = sbp.limit; + r_main_suffix(); + sbp.cursor = sbp.limit; + r_consonant_pair(); + sbp.cursor = sbp.limit; + r_other_suffix(); + sbp.cursor = sbp.limit; + r_undouble(); + return true; + } + }; + + /* and return a function that stems a word for the current locale */ + return function(token) { + // for lunr version 2 + if (typeof token.update === "function") { + return token.update(function(word) { + st.setCurrent(word); + st.stem(); + return st.getCurrent(); + }) + } else { // for lunr version <= 1 + st.setCurrent(token); + st.stem(); + return st.getCurrent(); + } + } + })(); + + lunr.Pipeline.registerFunction(lunr.da.stemmer, 'stemmer-da'); + + lunr.da.stopWordFilter = lunr.generateStopWordFilter('ad af alle alt anden at blev blive bliver da de dem den denne der deres det dette dig din disse dog du efter eller en end er et for fra ham han hans har havde have hende hendes her hos hun hvad hvis hvor i ikke ind jeg jer jo kunne man mange med meget men mig min mine mit mod ned noget nogle nu når og også om op os over på selv sig sin sine sit skal skulle som sådan thi til ud under var vi vil ville vor være været'.split(' ')); + + lunr.Pipeline.registerFunction(lunr.da.stopWordFilter, 'stopWordFilter-da'); + }; +})) \ No newline at end of file diff --git a/mkdocs/contrib/search/lunr-language/lunr.de.js b/mkdocs/contrib/search/lunr-language/lunr.de.js new file mode 100644 index 0000000000..9e32b0c397 --- /dev/null +++ b/mkdocs/contrib/search/lunr-language/lunr.de.js @@ -0,0 +1,384 @@ +/*! + * Lunr languages, `German` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +/** + * 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 environments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + factory()(root.lunr); + } +}(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 function(lunr) { + /* throw error if lunr is not yet included */ + if ('undefined' === typeof lunr) { + throw new Error('Lunr is not present. Please include / require Lunr before this script.'); + } + + /* throw error if lunr stemmer support is not yet included */ + if ('undefined' === typeof lunr.stemmerSupport) { + throw new Error('Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.'); + } + + /* register specific locale function */ + lunr.de = function() { + this.pipeline.reset(); + this.pipeline.add( + lunr.de.trimmer, + lunr.de.stopWordFilter, + lunr.de.stemmer + ); + + // for lunr version 2 + // this is necessary so that every searched word is also stemmed before + // in lunr <= 1 this is not needed, as it is done using the normal pipeline + if (this.searchPipeline) { + this.searchPipeline.reset(); + this.searchPipeline.add(lunr.de.stemmer) + } + }; + + /* lunr trimmer function */ + lunr.de.wordCharacters = "A-Za-z\xAA\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02B8\u02E0-\u02E4\u1D00-\u1D25\u1D2C-\u1D5C\u1D62-\u1D65\u1D6B-\u1D77\u1D79-\u1DBE\u1E00-\u1EFF\u2071\u207F\u2090-\u209C\u212A\u212B\u2132\u214E\u2160-\u2188\u2C60-\u2C7F\uA722-\uA787\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA7FF\uAB30-\uAB5A\uAB5C-\uAB64\uFB00-\uFB06\uFF21-\uFF3A\uFF41-\uFF5A"; + lunr.de.trimmer = lunr.trimmerSupport.generateTrimmer(lunr.de.wordCharacters); + + lunr.Pipeline.registerFunction(lunr.de.trimmer, 'trimmer-de'); + + /* lunr stemmer function */ + lunr.de.stemmer = (function() { + /* create the wrapped stemmer object */ + var Among = lunr.stemmerSupport.Among, + SnowballProgram = lunr.stemmerSupport.SnowballProgram, + st = new function GermanStemmer() { + var a_0 = [new Among("", -1, 6), new Among("U", 0, 2), + new Among("Y", 0, 1), new Among("\u00E4", 0, 3), + new Among("\u00F6", 0, 4), new Among("\u00FC", 0, 5) + ], + a_1 = [ + new Among("e", -1, 2), new Among("em", -1, 1), + new Among("en", -1, 2), new Among("ern", -1, 1), + new Among("er", -1, 1), new Among("s", -1, 3), + new Among("es", 5, 2) + ], + a_2 = [new Among("en", -1, 1), + new Among("er", -1, 1), new Among("st", -1, 2), + new Among("est", 2, 1) + ], + a_3 = [new Among("ig", -1, 1), + new Among("lich", -1, 1) + ], + a_4 = [new Among("end", -1, 1), + new Among("ig", -1, 2), new Among("ung", -1, 1), + new Among("lich", -1, 3), new Among("isch", -1, 2), + new Among("ik", -1, 2), new Among("heit", -1, 3), + new Among("keit", -1, 4) + ], + g_v = [17, 65, 16, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 8, 0, 32, 8 + ], + g_s_ending = [117, 30, 5], + g_st_ending = [ + 117, 30, 4 + ], + I_x, I_p2, I_p1, sbp = new SnowballProgram(); + this.setCurrent = function(word) { + sbp.setCurrent(word); + }; + this.getCurrent = function() { + return sbp.getCurrent(); + }; + + function habr1(c1, c2, v_1) { + if (sbp.eq_s(1, c1)) { + sbp.ket = sbp.cursor; + if (sbp.in_grouping(g_v, 97, 252)) { + sbp.slice_from(c2); + sbp.cursor = v_1; + return true; + } + } + return false; + } + + function r_prelude() { + var v_1 = sbp.cursor, + v_2, v_3, v_4, v_5; + while (true) { + v_2 = sbp.cursor; + sbp.bra = v_2; + if (sbp.eq_s(1, "\u00DF")) { + sbp.ket = sbp.cursor; + sbp.slice_from("ss"); + } else { + if (v_2 >= sbp.limit) + break; + sbp.cursor = v_2 + 1; + } + } + sbp.cursor = v_1; + while (true) { + v_3 = sbp.cursor; + while (true) { + v_4 = sbp.cursor; + if (sbp.in_grouping(g_v, 97, 252)) { + v_5 = sbp.cursor; + sbp.bra = v_5; + if (habr1("u", "U", v_4)) + break; + sbp.cursor = v_5; + if (habr1("y", "Y", v_4)) + break; + } + if (v_4 >= sbp.limit) { + sbp.cursor = v_3; + return; + } + sbp.cursor = v_4 + 1; + } + } + } + + function habr2() { + while (!sbp.in_grouping(g_v, 97, 252)) { + if (sbp.cursor >= sbp.limit) + return true; + sbp.cursor++; + } + while (!sbp.out_grouping(g_v, 97, 252)) { + if (sbp.cursor >= sbp.limit) + return true; + sbp.cursor++; + } + return false; + } + + function r_mark_regions() { + I_p1 = sbp.limit; + I_p2 = I_p1; + var c = sbp.cursor + 3; + if (0 <= c && c <= sbp.limit) { + I_x = c; + if (!habr2()) { + I_p1 = sbp.cursor; + if (I_p1 < I_x) + I_p1 = I_x; + if (!habr2()) + I_p2 = sbp.cursor; + } + } + } + + function r_postlude() { + var among_var, v_1; + while (true) { + v_1 = sbp.cursor; + sbp.bra = v_1; + among_var = sbp.find_among(a_0, 6); + if (!among_var) + return; + sbp.ket = sbp.cursor; + switch (among_var) { + case 1: + sbp.slice_from("y"); + break; + case 2: + case 5: + sbp.slice_from("u"); + break; + case 3: + sbp.slice_from("a"); + break; + case 4: + sbp.slice_from("o"); + break; + case 6: + if (sbp.cursor >= sbp.limit) + return; + sbp.cursor++; + break; + } + } + } + + function r_R1() { + return I_p1 <= sbp.cursor; + } + + function r_R2() { + return I_p2 <= sbp.cursor; + } + + function r_standard_suffix() { + var among_var, v_1 = sbp.limit - sbp.cursor, + v_2, v_3, v_4; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_1, 7); + if (among_var) { + sbp.bra = sbp.cursor; + if (r_R1()) { + switch (among_var) { + case 1: + sbp.slice_del(); + break; + case 2: + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(1, "s")) { + sbp.bra = sbp.cursor; + if (sbp.eq_s_b(3, "nis")) + sbp.slice_del(); + } + break; + case 3: + if (sbp.in_grouping_b(g_s_ending, 98, 116)) + sbp.slice_del(); + break; + } + } + } + sbp.cursor = sbp.limit - v_1; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_2, 4); + if (among_var) { + sbp.bra = sbp.cursor; + if (r_R1()) { + switch (among_var) { + case 1: + sbp.slice_del(); + break; + case 2: + if (sbp.in_grouping_b(g_st_ending, 98, 116)) { + var c = sbp.cursor - 3; + if (sbp.limit_backward <= c && c <= sbp.limit) { + sbp.cursor = c; + sbp.slice_del(); + } + } + break; + } + } + } + sbp.cursor = sbp.limit - v_1; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_4, 8); + if (among_var) { + sbp.bra = sbp.cursor; + if (r_R2()) { + switch (among_var) { + case 1: + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(2, "ig")) { + sbp.bra = sbp.cursor; + v_2 = sbp.limit - sbp.cursor; + if (!sbp.eq_s_b(1, "e")) { + sbp.cursor = sbp.limit - v_2; + if (r_R2()) + sbp.slice_del(); + } + } + break; + case 2: + v_3 = sbp.limit - sbp.cursor; + if (!sbp.eq_s_b(1, "e")) { + sbp.cursor = sbp.limit - v_3; + sbp.slice_del(); + } + break; + case 3: + sbp.slice_del(); + sbp.ket = sbp.cursor; + v_4 = sbp.limit - sbp.cursor; + if (!sbp.eq_s_b(2, "er")) { + sbp.cursor = sbp.limit - v_4; + if (!sbp.eq_s_b(2, "en")) + break; + } + sbp.bra = sbp.cursor; + if (r_R1()) + sbp.slice_del(); + break; + case 4: + sbp.slice_del(); + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_3, 2); + if (among_var) { + sbp.bra = sbp.cursor; + if (r_R2() && among_var == 1) + sbp.slice_del(); + } + break; + } + } + } + } + this.stem = function() { + var v_1 = sbp.cursor; + r_prelude(); + sbp.cursor = v_1; + r_mark_regions(); + sbp.limit_backward = v_1; + sbp.cursor = sbp.limit; + r_standard_suffix(); + sbp.cursor = sbp.limit_backward; + r_postlude(); + return true; + } + }; + + /* and return a function that stems a word for the current locale */ + return function(token) { + // for lunr version 2 + if (typeof token.update === "function") { + return token.update(function(word) { + st.setCurrent(word); + st.stem(); + return st.getCurrent(); + }) + } else { // for lunr version <= 1 + st.setCurrent(token); + st.stem(); + return st.getCurrent(); + } + } + })(); + + lunr.Pipeline.registerFunction(lunr.de.stemmer, 'stemmer-de'); + + lunr.de.stopWordFilter = lunr.generateStopWordFilter('aber alle allem allen aller alles als also am an ander andere anderem anderen anderer anderes anderm andern anderr anders auch auf aus bei bin bis bist da damit dann das dasselbe dazu daß dein deine deinem deinen deiner deines dem demselben den denn denselben der derer derselbe derselben des desselben dessen dich die dies diese dieselbe dieselben diesem diesen dieser dieses dir doch dort du durch ein eine einem einen einer eines einig einige einigem einigen einiger einiges einmal er es etwas euch euer eure eurem euren eurer eures für gegen gewesen hab habe haben hat hatte hatten hier hin hinter ich ihm ihn ihnen ihr ihre ihrem ihren ihrer ihres im in indem ins ist jede jedem jeden jeder jedes jene jenem jenen jener jenes jetzt kann kein keine keinem keinen keiner keines können könnte machen man manche manchem manchen mancher manches mein meine meinem meinen meiner meines mich mir mit muss musste nach nicht nichts noch nun nur ob oder ohne sehr sein seine seinem seinen seiner seines selbst sich sie sind so solche solchem solchen solcher solches soll sollte sondern sonst um und uns unse unsem unsen unser unses unter viel vom von vor war waren warst was weg weil weiter welche welchem welchen welcher welches wenn werde werden wie wieder will wir wird wirst wo wollen wollte während würde würden zu zum zur zwar zwischen über'.split(' ')); + + lunr.Pipeline.registerFunction(lunr.de.stopWordFilter, 'stopWordFilter-de'); + }; +})) \ No newline at end of file diff --git a/mkdocs/contrib/search/lunr-language/lunr.du.js b/mkdocs/contrib/search/lunr-language/lunr.du.js new file mode 100644 index 0000000000..a7ddf975c8 --- /dev/null +++ b/mkdocs/contrib/search/lunr-language/lunr.du.js @@ -0,0 +1,448 @@ +/*! + * Lunr languages, `Dutch` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +/** + * 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 environments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + factory()(root.lunr); + } +}(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 function(lunr) { + /* throw error if lunr is not yet included */ + if ('undefined' === typeof lunr) { + throw new Error('Lunr is not present. Please include / require Lunr before this script.'); + } + + /* throw error if lunr stemmer support is not yet included */ + if ('undefined' === typeof lunr.stemmerSupport) { + throw new Error('Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.'); + } + + /* register specific locale function */ + lunr.du = function() { + this.pipeline.reset(); + this.pipeline.add( + lunr.du.trimmer, + lunr.du.stopWordFilter, + lunr.du.stemmer + ); + + // for lunr version 2 + // this is necessary so that every searched word is also stemmed before + // in lunr <= 1 this is not needed, as it is done using the normal pipeline + if (this.searchPipeline) { + this.searchPipeline.reset(); + this.searchPipeline.add(lunr.du.stemmer) + } + }; + + /* lunr trimmer function */ + lunr.du.wordCharacters = "A-Za-z\xAA\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02B8\u02E0-\u02E4\u1D00-\u1D25\u1D2C-\u1D5C\u1D62-\u1D65\u1D6B-\u1D77\u1D79-\u1DBE\u1E00-\u1EFF\u2071\u207F\u2090-\u209C\u212A\u212B\u2132\u214E\u2160-\u2188\u2C60-\u2C7F\uA722-\uA787\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA7FF\uAB30-\uAB5A\uAB5C-\uAB64\uFB00-\uFB06\uFF21-\uFF3A\uFF41-\uFF5A"; + lunr.du.trimmer = lunr.trimmerSupport.generateTrimmer(lunr.du.wordCharacters); + + lunr.Pipeline.registerFunction(lunr.du.trimmer, 'trimmer-du'); + + /* lunr stemmer function */ + lunr.du.stemmer = (function() { + /* create the wrapped stemmer object */ + var Among = lunr.stemmerSupport.Among, + SnowballProgram = lunr.stemmerSupport.SnowballProgram, + st = new function DutchStemmer() { + var a_0 = [new Among("", -1, 6), new Among("\u00E1", 0, 1), + new Among("\u00E4", 0, 1), new Among("\u00E9", 0, 2), + new Among("\u00EB", 0, 2), new Among("\u00ED", 0, 3), + new Among("\u00EF", 0, 3), new Among("\u00F3", 0, 4), + new Among("\u00F6", 0, 4), new Among("\u00FA", 0, 5), + new Among("\u00FC", 0, 5) + ], + a_1 = [new Among("", -1, 3), + new Among("I", 0, 2), new Among("Y", 0, 1) + ], + a_2 = [ + new Among("dd", -1, -1), new Among("kk", -1, -1), + new Among("tt", -1, -1) + ], + a_3 = [new Among("ene", -1, 2), + new Among("se", -1, 3), new Among("en", -1, 2), + new Among("heden", 2, 1), new Among("s", -1, 3) + ], + a_4 = [ + new Among("end", -1, 1), new Among("ig", -1, 2), + new Among("ing", -1, 1), new Among("lijk", -1, 3), + new Among("baar", -1, 4), new Among("bar", -1, 5) + ], + a_5 = [ + new Among("aa", -1, -1), new Among("ee", -1, -1), + new Among("oo", -1, -1), new Among("uu", -1, -1) + ], + g_v = [17, 65, + 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128 + ], + g_v_I = [1, 0, 0, + 17, 65, 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128 + ], + g_v_j = [ + 17, 67, 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128 + ], + I_p2, I_p1, B_e_found, sbp = new SnowballProgram(); + this.setCurrent = function(word) { + sbp.setCurrent(word); + }; + this.getCurrent = function() { + return sbp.getCurrent(); + }; + + function r_prelude() { + var among_var, v_1 = sbp.cursor, + v_2, v_3; + while (true) { + sbp.bra = sbp.cursor; + among_var = sbp.find_among(a_0, 11); + if (among_var) { + sbp.ket = sbp.cursor; + switch (among_var) { + case 1: + sbp.slice_from("a"); + continue; + case 2: + sbp.slice_from("e"); + continue; + case 3: + sbp.slice_from("i"); + continue; + case 4: + sbp.slice_from("o"); + continue; + case 5: + sbp.slice_from("u"); + continue; + case 6: + if (sbp.cursor >= sbp.limit) + break; + sbp.cursor++; + continue; + } + } + break; + } + sbp.cursor = v_1; + sbp.bra = v_1; + if (sbp.eq_s(1, "y")) { + sbp.ket = sbp.cursor; + sbp.slice_from("Y"); + } else + sbp.cursor = v_1; + while (true) { + v_2 = sbp.cursor; + if (sbp.in_grouping(g_v, 97, 232)) { + v_3 = sbp.cursor; + sbp.bra = v_3; + if (sbp.eq_s(1, "i")) { + sbp.ket = sbp.cursor; + if (sbp.in_grouping(g_v, 97, 232)) { + sbp.slice_from("I"); + sbp.cursor = v_2; + } + } else { + sbp.cursor = v_3; + if (sbp.eq_s(1, "y")) { + sbp.ket = sbp.cursor; + sbp.slice_from("Y"); + sbp.cursor = v_2; + } else if (habr1(v_2)) + break; + } + } else if (habr1(v_2)) + break; + } + } + + function habr1(v_1) { + sbp.cursor = v_1; + if (v_1 >= sbp.limit) + return true; + sbp.cursor++; + return false; + } + + function r_mark_regions() { + I_p1 = sbp.limit; + I_p2 = I_p1; + if (!habr2()) { + I_p1 = sbp.cursor; + if (I_p1 < 3) + I_p1 = 3; + if (!habr2()) + I_p2 = sbp.cursor; + } + } + + function habr2() { + while (!sbp.in_grouping(g_v, 97, 232)) { + if (sbp.cursor >= sbp.limit) + return true; + sbp.cursor++; + } + while (!sbp.out_grouping(g_v, 97, 232)) { + if (sbp.cursor >= sbp.limit) + return true; + sbp.cursor++; + } + return false; + } + + function r_postlude() { + var among_var; + while (true) { + sbp.bra = sbp.cursor; + among_var = sbp.find_among(a_1, 3); + if (among_var) { + sbp.ket = sbp.cursor; + switch (among_var) { + case 1: + sbp.slice_from("y"); + break; + case 2: + sbp.slice_from("i"); + break; + case 3: + if (sbp.cursor >= sbp.limit) + return; + sbp.cursor++; + break; + } + } + } + } + + function r_R1() { + return I_p1 <= sbp.cursor; + } + + function r_R2() { + return I_p2 <= sbp.cursor; + } + + function r_undouble() { + var v_1 = sbp.limit - sbp.cursor; + if (sbp.find_among_b(a_2, 3)) { + sbp.cursor = sbp.limit - v_1; + sbp.ket = sbp.cursor; + if (sbp.cursor > sbp.limit_backward) { + sbp.cursor--; + sbp.bra = sbp.cursor; + sbp.slice_del(); + } + } + } + + function r_e_ending() { + var v_1; + B_e_found = false; + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(1, "e")) { + sbp.bra = sbp.cursor; + if (r_R1()) { + v_1 = sbp.limit - sbp.cursor; + if (sbp.out_grouping_b(g_v, 97, 232)) { + sbp.cursor = sbp.limit - v_1; + sbp.slice_del(); + B_e_found = true; + r_undouble(); + } + } + } + } + + function r_en_ending() { + var v_1; + if (r_R1()) { + v_1 = sbp.limit - sbp.cursor; + if (sbp.out_grouping_b(g_v, 97, 232)) { + sbp.cursor = sbp.limit - v_1; + if (!sbp.eq_s_b(3, "gem")) { + sbp.cursor = sbp.limit - v_1; + sbp.slice_del(); + r_undouble(); + } + } + } + } + + function r_standard_suffix() { + var among_var, v_1 = sbp.limit - sbp.cursor, + v_2, v_3, v_4, v_5, v_6; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_3, 5); + if (among_var) { + sbp.bra = sbp.cursor; + switch (among_var) { + case 1: + if (r_R1()) + sbp.slice_from("heid"); + break; + case 2: + r_en_ending(); + break; + case 3: + if (r_R1() && sbp.out_grouping_b(g_v_j, 97, 232)) + sbp.slice_del(); + break; + } + } + sbp.cursor = sbp.limit - v_1; + r_e_ending(); + sbp.cursor = sbp.limit - v_1; + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(4, "heid")) { + sbp.bra = sbp.cursor; + if (r_R2()) { + v_2 = sbp.limit - sbp.cursor; + if (!sbp.eq_s_b(1, "c")) { + sbp.cursor = sbp.limit - v_2; + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(2, "en")) { + sbp.bra = sbp.cursor; + r_en_ending(); + } + } + } + } + sbp.cursor = sbp.limit - v_1; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_4, 6); + if (among_var) { + sbp.bra = sbp.cursor; + switch (among_var) { + case 1: + if (r_R2()) { + sbp.slice_del(); + v_3 = sbp.limit - sbp.cursor; + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(2, "ig")) { + sbp.bra = sbp.cursor; + if (r_R2()) { + v_4 = sbp.limit - sbp.cursor; + if (!sbp.eq_s_b(1, "e")) { + sbp.cursor = sbp.limit - v_4; + sbp.slice_del(); + break; + } + } + } + sbp.cursor = sbp.limit - v_3; + r_undouble(); + } + break; + case 2: + if (r_R2()) { + v_5 = sbp.limit - sbp.cursor; + if (!sbp.eq_s_b(1, "e")) { + sbp.cursor = sbp.limit - v_5; + sbp.slice_del(); + } + } + break; + case 3: + if (r_R2()) { + sbp.slice_del(); + r_e_ending(); + } + break; + case 4: + if (r_R2()) + sbp.slice_del(); + break; + case 5: + if (r_R2() && B_e_found) + sbp.slice_del(); + break; + } + } + sbp.cursor = sbp.limit - v_1; + if (sbp.out_grouping_b(g_v_I, 73, 232)) { + v_6 = sbp.limit - sbp.cursor; + if (sbp.find_among_b(a_5, 4) && sbp.out_grouping_b(g_v, 97, 232)) { + sbp.cursor = sbp.limit - v_6; + sbp.ket = sbp.cursor; + if (sbp.cursor > sbp.limit_backward) { + sbp.cursor--; + sbp.bra = sbp.cursor; + sbp.slice_del(); + } + } + } + } + this.stem = function() { + var v_1 = sbp.cursor; + r_prelude(); + sbp.cursor = v_1; + r_mark_regions(); + sbp.limit_backward = v_1; + sbp.cursor = sbp.limit; + r_standard_suffix(); + sbp.cursor = sbp.limit_backward; + r_postlude(); + return true; + } + }; + + /* and return a function that stems a word for the current locale */ + return function(token) { + // for lunr version 2 + if (typeof token.update === "function") { + return token.update(function(word) { + st.setCurrent(word); + st.stem(); + return st.getCurrent(); + }) + } else { // for lunr version <= 1 + st.setCurrent(token); + st.stem(); + return st.getCurrent(); + } + } + })(); + + lunr.Pipeline.registerFunction(lunr.du.stemmer, 'stemmer-du'); + + lunr.du.stopWordFilter = lunr.generateStopWordFilter(' aan al alles als altijd andere ben bij daar dan dat de der deze die dit doch doen door dus een eens en er ge geen geweest haar had heb hebben heeft hem het hier hij hoe hun iemand iets ik in is ja je kan kon kunnen maar me meer men met mij mijn moet na naar niet niets nog nu of om omdat onder ons ook op over reeds te tegen toch toen tot u uit uw van veel voor want waren was wat werd wezen wie wil worden wordt zal ze zelf zich zij zijn zo zonder zou'.split(' ')); + + lunr.Pipeline.registerFunction(lunr.du.stopWordFilter, 'stopWordFilter-du'); + }; +})) \ No newline at end of file diff --git a/mkdocs/contrib/search/lunr-language/lunr.es.js b/mkdocs/contrib/search/lunr-language/lunr.es.js new file mode 100644 index 0000000000..46cbc36070 --- /dev/null +++ b/mkdocs/contrib/search/lunr-language/lunr.es.js @@ -0,0 +1,599 @@ +/*! + * Lunr languages, `Spanish` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +/** + * 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 environments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + factory()(root.lunr); + } +}(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 function(lunr) { + /* throw error if lunr is not yet included */ + if ('undefined' === typeof lunr) { + throw new Error('Lunr is not present. Please include / require Lunr before this script.'); + } + + /* throw error if lunr stemmer support is not yet included */ + if ('undefined' === typeof lunr.stemmerSupport) { + throw new Error('Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.'); + } + + /* register specific locale function */ + lunr.es = function() { + this.pipeline.reset(); + this.pipeline.add( + lunr.es.trimmer, + lunr.es.stopWordFilter, + lunr.es.stemmer + ); + + // for lunr version 2 + // this is necessary so that every searched word is also stemmed before + // in lunr <= 1 this is not needed, as it is done using the normal pipeline + if (this.searchPipeline) { + this.searchPipeline.reset(); + this.searchPipeline.add(lunr.es.stemmer) + } + }; + + /* lunr trimmer function */ + lunr.es.wordCharacters = "A-Za-z\xAA\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02B8\u02E0-\u02E4\u1D00-\u1D25\u1D2C-\u1D5C\u1D62-\u1D65\u1D6B-\u1D77\u1D79-\u1DBE\u1E00-\u1EFF\u2071\u207F\u2090-\u209C\u212A\u212B\u2132\u214E\u2160-\u2188\u2C60-\u2C7F\uA722-\uA787\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA7FF\uAB30-\uAB5A\uAB5C-\uAB64\uFB00-\uFB06\uFF21-\uFF3A\uFF41-\uFF5A"; + lunr.es.trimmer = lunr.trimmerSupport.generateTrimmer(lunr.es.wordCharacters); + + lunr.Pipeline.registerFunction(lunr.es.trimmer, 'trimmer-es'); + + /* lunr stemmer function */ + lunr.es.stemmer = (function() { + /* create the wrapped stemmer object */ + var Among = lunr.stemmerSupport.Among, + SnowballProgram = lunr.stemmerSupport.SnowballProgram, + st = new function SpanishStemmer() { + var a_0 = [new Among("", -1, 6), new Among("\u00E1", 0, 1), + new Among("\u00E9", 0, 2), new Among("\u00ED", 0, 3), + new Among("\u00F3", 0, 4), new Among("\u00FA", 0, 5) + ], + a_1 = [ + new Among("la", -1, -1), new Among("sela", 0, -1), + new Among("le", -1, -1), new Among("me", -1, -1), + new Among("se", -1, -1), new Among("lo", -1, -1), + new Among("selo", 5, -1), new Among("las", -1, -1), + new Among("selas", 7, -1), new Among("les", -1, -1), + new Among("los", -1, -1), new Among("selos", 10, -1), + new Among("nos", -1, -1) + ], + a_2 = [new Among("ando", -1, 6), + new Among("iendo", -1, 6), new Among("yendo", -1, 7), + new Among("\u00E1ndo", -1, 2), new Among("i\u00E9ndo", -1, 1), + new Among("ar", -1, 6), new Among("er", -1, 6), + new Among("ir", -1, 6), new Among("\u00E1r", -1, 3), + new Among("\u00E9r", -1, 4), new Among("\u00EDr", -1, 5) + ], + a_3 = [ + new Among("ic", -1, -1), new Among("ad", -1, -1), + new Among("os", -1, -1), new Among("iv", -1, 1) + ], + a_4 = [ + new Among("able", -1, 1), new Among("ible", -1, 1), + new Among("ante", -1, 1) + ], + a_5 = [new Among("ic", -1, 1), + new Among("abil", -1, 1), new Among("iv", -1, 1) + ], + a_6 = [ + new Among("ica", -1, 1), new Among("ancia", -1, 2), + new Among("encia", -1, 5), new Among("adora", -1, 2), + new Among("osa", -1, 1), new Among("ista", -1, 1), + new Among("iva", -1, 9), new Among("anza", -1, 1), + new Among("log\u00EDa", -1, 3), new Among("idad", -1, 8), + new Among("able", -1, 1), new Among("ible", -1, 1), + new Among("ante", -1, 2), new Among("mente", -1, 7), + new Among("amente", 13, 6), new Among("aci\u00F3n", -1, 2), + new Among("uci\u00F3n", -1, 4), new Among("ico", -1, 1), + new Among("ismo", -1, 1), new Among("oso", -1, 1), + new Among("amiento", -1, 1), new Among("imiento", -1, 1), + new Among("ivo", -1, 9), new Among("ador", -1, 2), + new Among("icas", -1, 1), new Among("ancias", -1, 2), + new Among("encias", -1, 5), new Among("adoras", -1, 2), + new Among("osas", -1, 1), new Among("istas", -1, 1), + new Among("ivas", -1, 9), new Among("anzas", -1, 1), + new Among("log\u00EDas", -1, 3), new Among("idades", -1, 8), + new Among("ables", -1, 1), new Among("ibles", -1, 1), + new Among("aciones", -1, 2), new Among("uciones", -1, 4), + new Among("adores", -1, 2), new Among("antes", -1, 2), + new Among("icos", -1, 1), new Among("ismos", -1, 1), + new Among("osos", -1, 1), new Among("amientos", -1, 1), + new Among("imientos", -1, 1), new Among("ivos", -1, 9) + ], + a_7 = [ + new Among("ya", -1, 1), new Among("ye", -1, 1), + new Among("yan", -1, 1), new Among("yen", -1, 1), + new Among("yeron", -1, 1), new Among("yendo", -1, 1), + new Among("yo", -1, 1), new Among("yas", -1, 1), + new Among("yes", -1, 1), new Among("yais", -1, 1), + new Among("yamos", -1, 1), new Among("y\u00F3", -1, 1) + ], + a_8 = [ + new Among("aba", -1, 2), new Among("ada", -1, 2), + new Among("ida", -1, 2), new Among("ara", -1, 2), + new Among("iera", -1, 2), new Among("\u00EDa", -1, 2), + new Among("ar\u00EDa", 5, 2), new Among("er\u00EDa", 5, 2), + new Among("ir\u00EDa", 5, 2), new Among("ad", -1, 2), + new Among("ed", -1, 2), new Among("id", -1, 2), + new Among("ase", -1, 2), new Among("iese", -1, 2), + new Among("aste", -1, 2), new Among("iste", -1, 2), + new Among("an", -1, 2), new Among("aban", 16, 2), + new Among("aran", 16, 2), new Among("ieran", 16, 2), + new Among("\u00EDan", 16, 2), new Among("ar\u00EDan", 20, 2), + new Among("er\u00EDan", 20, 2), new Among("ir\u00EDan", 20, 2), + new Among("en", -1, 1), new Among("asen", 24, 2), + new Among("iesen", 24, 2), new Among("aron", -1, 2), + new Among("ieron", -1, 2), new Among("ar\u00E1n", -1, 2), + new Among("er\u00E1n", -1, 2), new Among("ir\u00E1n", -1, 2), + new Among("ado", -1, 2), new Among("ido", -1, 2), + new Among("ando", -1, 2), new Among("iendo", -1, 2), + new Among("ar", -1, 2), new Among("er", -1, 2), + new Among("ir", -1, 2), new Among("as", -1, 2), + new Among("abas", 39, 2), new Among("adas", 39, 2), + new Among("idas", 39, 2), new Among("aras", 39, 2), + new Among("ieras", 39, 2), new Among("\u00EDas", 39, 2), + new Among("ar\u00EDas", 45, 2), new Among("er\u00EDas", 45, 2), + new Among("ir\u00EDas", 45, 2), new Among("es", -1, 1), + new Among("ases", 49, 2), new Among("ieses", 49, 2), + new Among("abais", -1, 2), new Among("arais", -1, 2), + new Among("ierais", -1, 2), new Among("\u00EDais", -1, 2), + new Among("ar\u00EDais", 55, 2), new Among("er\u00EDais", 55, 2), + new Among("ir\u00EDais", 55, 2), new Among("aseis", -1, 2), + new Among("ieseis", -1, 2), new Among("asteis", -1, 2), + new Among("isteis", -1, 2), new Among("\u00E1is", -1, 2), + new Among("\u00E9is", -1, 1), new Among("ar\u00E9is", 64, 2), + new Among("er\u00E9is", 64, 2), new Among("ir\u00E9is", 64, 2), + new Among("ados", -1, 2), new Among("idos", -1, 2), + new Among("amos", -1, 2), new Among("\u00E1bamos", 70, 2), + new Among("\u00E1ramos", 70, 2), new Among("i\u00E9ramos", 70, 2), + new Among("\u00EDamos", 70, 2), new Among("ar\u00EDamos", 74, 2), + new Among("er\u00EDamos", 74, 2), new Among("ir\u00EDamos", 74, 2), + new Among("emos", -1, 1), new Among("aremos", 78, 2), + new Among("eremos", 78, 2), new Among("iremos", 78, 2), + new Among("\u00E1semos", 78, 2), new Among("i\u00E9semos", 78, 2), + new Among("imos", -1, 2), new Among("ar\u00E1s", -1, 2), + new Among("er\u00E1s", -1, 2), new Among("ir\u00E1s", -1, 2), + new Among("\u00EDs", -1, 2), new Among("ar\u00E1", -1, 2), + new Among("er\u00E1", -1, 2), new Among("ir\u00E1", -1, 2), + new Among("ar\u00E9", -1, 2), new Among("er\u00E9", -1, 2), + new Among("ir\u00E9", -1, 2), new Among("i\u00F3", -1, 2) + ], + a_9 = [ + new Among("a", -1, 1), new Among("e", -1, 2), + new Among("o", -1, 1), new Among("os", -1, 1), + new Among("\u00E1", -1, 1), new Among("\u00E9", -1, 2), + new Among("\u00ED", -1, 1), new Among("\u00F3", -1, 1) + ], + g_v = [17, + 65, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 17, 4, 10 + ], + I_p2, I_p1, I_pV, sbp = new SnowballProgram(); + this.setCurrent = function(word) { + sbp.setCurrent(word); + }; + this.getCurrent = function() { + return sbp.getCurrent(); + }; + + function habr1() { + if (sbp.out_grouping(g_v, 97, 252)) { + while (!sbp.in_grouping(g_v, 97, 252)) { + if (sbp.cursor >= sbp.limit) + return true; + sbp.cursor++; + } + return false; + } + return true; + } + + function habr2() { + if (sbp.in_grouping(g_v, 97, 252)) { + var v_1 = sbp.cursor; + if (habr1()) { + sbp.cursor = v_1; + if (!sbp.in_grouping(g_v, 97, 252)) + return true; + while (!sbp.out_grouping(g_v, 97, 252)) { + if (sbp.cursor >= sbp.limit) + return true; + sbp.cursor++; + } + } + return false; + } + return true; + } + + function habr3() { + var v_1 = sbp.cursor, + v_2; + if (habr2()) { + sbp.cursor = v_1; + if (!sbp.out_grouping(g_v, 97, 252)) + return; + v_2 = sbp.cursor; + if (habr1()) { + sbp.cursor = v_2; + if (!sbp.in_grouping(g_v, 97, 252) || sbp.cursor >= sbp.limit) + return; + sbp.cursor++; + } + } + I_pV = sbp.cursor; + } + + function habr4() { + while (!sbp.in_grouping(g_v, 97, 252)) { + if (sbp.cursor >= sbp.limit) + return false; + sbp.cursor++; + } + while (!sbp.out_grouping(g_v, 97, 252)) { + if (sbp.cursor >= sbp.limit) + return false; + sbp.cursor++; + } + return true; + } + + function r_mark_regions() { + var v_1 = sbp.cursor; + I_pV = sbp.limit; + I_p1 = I_pV; + I_p2 = I_pV; + habr3(); + sbp.cursor = v_1; + if (habr4()) { + I_p1 = sbp.cursor; + if (habr4()) + I_p2 = sbp.cursor; + } + } + + function r_postlude() { + var among_var; + while (true) { + sbp.bra = sbp.cursor; + among_var = sbp.find_among(a_0, 6); + if (among_var) { + sbp.ket = sbp.cursor; + switch (among_var) { + case 1: + sbp.slice_from("a"); + continue; + case 2: + sbp.slice_from("e"); + continue; + case 3: + sbp.slice_from("i"); + continue; + case 4: + sbp.slice_from("o"); + continue; + case 5: + sbp.slice_from("u"); + continue; + case 6: + if (sbp.cursor >= sbp.limit) + break; + sbp.cursor++; + continue; + } + } + break; + } + } + + function r_RV() { + return I_pV <= sbp.cursor; + } + + function r_R1() { + return I_p1 <= sbp.cursor; + } + + function r_R2() { + return I_p2 <= sbp.cursor; + } + + function r_attached_pronoun() { + var among_var; + sbp.ket = sbp.cursor; + if (sbp.find_among_b(a_1, 13)) { + sbp.bra = sbp.cursor; + among_var = sbp.find_among_b(a_2, 11); + if (among_var && r_RV()) + switch (among_var) { + case 1: + sbp.bra = sbp.cursor; + sbp.slice_from("iendo"); + break; + case 2: + sbp.bra = sbp.cursor; + sbp.slice_from("ando"); + break; + case 3: + sbp.bra = sbp.cursor; + sbp.slice_from("ar"); + break; + case 4: + sbp.bra = sbp.cursor; + sbp.slice_from("er"); + break; + case 5: + sbp.bra = sbp.cursor; + sbp.slice_from("ir"); + break; + case 6: + sbp.slice_del(); + break; + case 7: + if (sbp.eq_s_b(1, "u")) + sbp.slice_del(); + break; + } + } + } + + function habr5(a, n) { + if (!r_R2()) + return true; + sbp.slice_del(); + sbp.ket = sbp.cursor; + var among_var = sbp.find_among_b(a, n); + if (among_var) { + sbp.bra = sbp.cursor; + if (among_var == 1 && r_R2()) + sbp.slice_del(); + } + return false; + } + + function habr6(c1) { + if (!r_R2()) + return true; + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(2, c1)) { + sbp.bra = sbp.cursor; + if (r_R2()) + sbp.slice_del(); + } + return false; + } + + function r_standard_suffix() { + var among_var; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_6, 46); + if (among_var) { + sbp.bra = sbp.cursor; + switch (among_var) { + case 1: + if (!r_R2()) + return false; + sbp.slice_del(); + break; + case 2: + if (habr6("ic")) + return false; + break; + case 3: + if (!r_R2()) + return false; + sbp.slice_from("log"); + break; + case 4: + if (!r_R2()) + return false; + sbp.slice_from("u"); + break; + case 5: + if (!r_R2()) + return false; + sbp.slice_from("ente"); + break; + case 6: + if (!r_R1()) + return false; + sbp.slice_del(); + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_3, 4); + if (among_var) { + sbp.bra = sbp.cursor; + if (r_R2()) { + sbp.slice_del(); + if (among_var == 1) { + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(2, "at")) { + sbp.bra = sbp.cursor; + if (r_R2()) + sbp.slice_del(); + } + } + } + } + break; + case 7: + if (habr5(a_4, 3)) + return false; + break; + case 8: + if (habr5(a_5, 3)) + return false; + break; + case 9: + if (habr6("at")) + return false; + break; + } + return true; + } + return false; + } + + function r_y_verb_suffix() { + var among_var, v_1; + if (sbp.cursor >= I_pV) { + v_1 = sbp.limit_backward; + sbp.limit_backward = I_pV; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_7, 12); + sbp.limit_backward = v_1; + if (among_var) { + sbp.bra = sbp.cursor; + if (among_var == 1) { + if (!sbp.eq_s_b(1, "u")) + return false; + sbp.slice_del(); + } + return true; + } + } + return false; + } + + function r_verb_suffix() { + var among_var, v_1, v_2, v_3; + if (sbp.cursor >= I_pV) { + v_1 = sbp.limit_backward; + sbp.limit_backward = I_pV; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_8, 96); + sbp.limit_backward = v_1; + if (among_var) { + sbp.bra = sbp.cursor; + switch (among_var) { + case 1: + v_2 = sbp.limit - sbp.cursor; + if (sbp.eq_s_b(1, "u")) { + v_3 = sbp.limit - sbp.cursor; + if (sbp.eq_s_b(1, "g")) + sbp.cursor = sbp.limit - v_3; + else + sbp.cursor = sbp.limit - v_2; + } else + sbp.cursor = sbp.limit - v_2; + sbp.bra = sbp.cursor; + case 2: + sbp.slice_del(); + break; + } + } + } + } + + function r_residual_suffix() { + var among_var, v_1; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_9, 8); + if (among_var) { + sbp.bra = sbp.cursor; + switch (among_var) { + case 1: + if (r_RV()) + sbp.slice_del(); + break; + case 2: + if (r_RV()) { + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(1, "u")) { + sbp.bra = sbp.cursor; + v_1 = sbp.limit - sbp.cursor; + if (sbp.eq_s_b(1, "g")) { + sbp.cursor = sbp.limit - v_1; + if (r_RV()) + sbp.slice_del(); + } + } + } + break; + } + } + } + this.stem = function() { + var v_1 = sbp.cursor; + r_mark_regions(); + sbp.limit_backward = v_1; + sbp.cursor = sbp.limit; + r_attached_pronoun(); + sbp.cursor = sbp.limit; + if (!r_standard_suffix()) { + sbp.cursor = sbp.limit; + if (!r_y_verb_suffix()) { + sbp.cursor = sbp.limit; + r_verb_suffix(); + } + } + sbp.cursor = sbp.limit; + r_residual_suffix(); + sbp.cursor = sbp.limit_backward; + r_postlude(); + return true; + } + }; + + /* and return a function that stems a word for the current locale */ + return function(token) { + // for lunr version 2 + if (typeof token.update === "function") { + return token.update(function(word) { + st.setCurrent(word); + st.stem(); + return st.getCurrent(); + }) + } else { // for lunr version <= 1 + st.setCurrent(token); + st.stem(); + return st.getCurrent(); + } + } + })(); + + lunr.Pipeline.registerFunction(lunr.es.stemmer, 'stemmer-es'); + + lunr.es.stopWordFilter = lunr.generateStopWordFilter('a al algo algunas algunos ante antes como con contra cual cuando de del desde donde durante e el ella ellas ellos en entre era erais eran eras eres es esa esas ese eso esos esta estaba estabais estaban estabas estad estada estadas estado estados estamos estando estar estaremos estará estarán estarás estaré estaréis estaría estaríais estaríamos estarían estarías estas este estemos esto estos estoy estuve estuviera estuvierais estuvieran estuvieras estuvieron estuviese estuvieseis estuviesen estuvieses estuvimos estuviste estuvisteis estuviéramos estuviésemos estuvo está estábamos estáis están estás esté estéis estén estés fue fuera fuerais fueran fueras fueron fuese fueseis fuesen fueses fui fuimos fuiste fuisteis fuéramos fuésemos ha habida habidas habido habidos habiendo habremos habrá habrán habrás habré habréis habría habríais habríamos habrían habrías habéis había habíais habíamos habían habías han has hasta hay haya hayamos hayan hayas hayáis he hemos hube hubiera hubierais hubieran hubieras hubieron hubiese hubieseis hubiesen hubieses hubimos hubiste hubisteis hubiéramos hubiésemos hubo la las le les lo los me mi mis mucho muchos muy más mí mía mías mío míos nada ni no nos nosotras nosotros nuestra nuestras nuestro nuestros o os otra otras otro otros para pero poco por porque que quien quienes qué se sea seamos sean seas seremos será serán serás seré seréis sería seríais seríamos serían serías seáis sido siendo sin sobre sois somos son soy su sus suya suyas suyo suyos sí también tanto te tendremos tendrá tendrán tendrás tendré tendréis tendría tendríais tendríamos tendrían tendrías tened tenemos tenga tengamos tengan tengas tengo tengáis tenida tenidas tenido tenidos teniendo tenéis tenía teníais teníamos tenían tenías ti tiene tienen tienes todo todos tu tus tuve tuviera tuvierais tuvieran tuvieras tuvieron tuviese tuvieseis tuviesen tuvieses tuvimos tuviste tuvisteis tuviéramos tuviésemos tuvo tuya tuyas tuyo tuyos tú un una uno unos vosotras vosotros vuestra vuestras vuestro vuestros y ya yo él éramos'.split(' ')); + + lunr.Pipeline.registerFunction(lunr.es.stopWordFilter, 'stopWordFilter-es'); + }; +})) \ No newline at end of file diff --git a/mkdocs/contrib/search/lunr-language/lunr.fi.js b/mkdocs/contrib/search/lunr-language/lunr.fi.js new file mode 100644 index 0000000000..7ce9515404 --- /dev/null +++ b/mkdocs/contrib/search/lunr-language/lunr.fi.js @@ -0,0 +1,541 @@ +/*! + * Lunr languages, `Finnish` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +/** + * 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 environments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + factory()(root.lunr); + } +}(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 function(lunr) { + /* throw error if lunr is not yet included */ + if ('undefined' === typeof lunr) { + throw new Error('Lunr is not present. Please include / require Lunr before this script.'); + } + + /* throw error if lunr stemmer support is not yet included */ + if ('undefined' === typeof lunr.stemmerSupport) { + throw new Error('Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.'); + } + + /* register specific locale function */ + lunr.fi = function() { + this.pipeline.reset(); + this.pipeline.add( + lunr.fi.trimmer, + lunr.fi.stopWordFilter, + lunr.fi.stemmer + ); + + // for lunr version 2 + // this is necessary so that every searched word is also stemmed before + // in lunr <= 1 this is not needed, as it is done using the normal pipeline + if (this.searchPipeline) { + this.searchPipeline.reset(); + this.searchPipeline.add(lunr.fi.stemmer) + } + }; + + /* lunr trimmer function */ + lunr.fi.wordCharacters = "A-Za-z\xAA\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02B8\u02E0-\u02E4\u1D00-\u1D25\u1D2C-\u1D5C\u1D62-\u1D65\u1D6B-\u1D77\u1D79-\u1DBE\u1E00-\u1EFF\u2071\u207F\u2090-\u209C\u212A\u212B\u2132\u214E\u2160-\u2188\u2C60-\u2C7F\uA722-\uA787\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA7FF\uAB30-\uAB5A\uAB5C-\uAB64\uFB00-\uFB06\uFF21-\uFF3A\uFF41-\uFF5A"; + lunr.fi.trimmer = lunr.trimmerSupport.generateTrimmer(lunr.fi.wordCharacters); + + lunr.Pipeline.registerFunction(lunr.fi.trimmer, 'trimmer-fi'); + + /* lunr stemmer function */ + lunr.fi.stemmer = (function() { + /* create the wrapped stemmer object */ + var Among = lunr.stemmerSupport.Among, + SnowballProgram = lunr.stemmerSupport.SnowballProgram, + st = new function FinnishStemmer() { + var a_0 = [new Among("pa", -1, 1), new Among("sti", -1, 2), + new Among("kaan", -1, 1), new Among("han", -1, 1), + new Among("kin", -1, 1), new Among("h\u00E4n", -1, 1), + new Among("k\u00E4\u00E4n", -1, 1), new Among("ko", -1, 1), + new Among("p\u00E4", -1, 1), new Among("k\u00F6", -1, 1) + ], + a_1 = [ + new Among("lla", -1, -1), new Among("na", -1, -1), + new Among("ssa", -1, -1), new Among("ta", -1, -1), + new Among("lta", 3, -1), new Among("sta", 3, -1) + ], + a_2 = [ + new Among("ll\u00E4", -1, -1), new Among("n\u00E4", -1, -1), + new Among("ss\u00E4", -1, -1), new Among("t\u00E4", -1, -1), + new Among("lt\u00E4", 3, -1), new Among("st\u00E4", 3, -1) + ], + a_3 = [ + new Among("lle", -1, -1), new Among("ine", -1, -1) + ], + a_4 = [ + new Among("nsa", -1, 3), new Among("mme", -1, 3), + new Among("nne", -1, 3), new Among("ni", -1, 2), + new Among("si", -1, 1), new Among("an", -1, 4), + new Among("en", -1, 6), new Among("\u00E4n", -1, 5), + new Among("ns\u00E4", -1, 3) + ], + a_5 = [new Among("aa", -1, -1), + new Among("ee", -1, -1), new Among("ii", -1, -1), + new Among("oo", -1, -1), new Among("uu", -1, -1), + new Among("\u00E4\u00E4", -1, -1), + new Among("\u00F6\u00F6", -1, -1) + ], + a_6 = [new Among("a", -1, 8), + new Among("lla", 0, -1), new Among("na", 0, -1), + new Among("ssa", 0, -1), new Among("ta", 0, -1), + new Among("lta", 4, -1), new Among("sta", 4, -1), + new Among("tta", 4, 9), new Among("lle", -1, -1), + new Among("ine", -1, -1), new Among("ksi", -1, -1), + new Among("n", -1, 7), new Among("han", 11, 1), + new Among("den", 11, -1, r_VI), new Among("seen", 11, -1, r_LONG), + new Among("hen", 11, 2), new Among("tten", 11, -1, r_VI), + new Among("hin", 11, 3), new Among("siin", 11, -1, r_VI), + new Among("hon", 11, 4), new Among("h\u00E4n", 11, 5), + new Among("h\u00F6n", 11, 6), new Among("\u00E4", -1, 8), + new Among("ll\u00E4", 22, -1), new Among("n\u00E4", 22, -1), + new Among("ss\u00E4", 22, -1), new Among("t\u00E4", 22, -1), + new Among("lt\u00E4", 26, -1), new Among("st\u00E4", 26, -1), + new Among("tt\u00E4", 26, 9) + ], + a_7 = [new Among("eja", -1, -1), + new Among("mma", -1, 1), new Among("imma", 1, -1), + new Among("mpa", -1, 1), new Among("impa", 3, -1), + new Among("mmi", -1, 1), new Among("immi", 5, -1), + new Among("mpi", -1, 1), new Among("impi", 7, -1), + new Among("ej\u00E4", -1, -1), new Among("mm\u00E4", -1, 1), + new Among("imm\u00E4", 10, -1), new Among("mp\u00E4", -1, 1), + new Among("imp\u00E4", 12, -1) + ], + a_8 = [new Among("i", -1, -1), + new Among("j", -1, -1) + ], + a_9 = [new Among("mma", -1, 1), + new Among("imma", 0, -1) + ], + g_AEI = [17, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 8 + ], + g_V1 = [17, 65, 16, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 8, 0, 32 + ], + g_V2 = [17, 65, 16, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 8, 0, 32 + ], + g_particle_end = [17, 97, 24, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 32 + ], + B_ending_removed, S_x, I_p2, I_p1, sbp = new SnowballProgram(); + this.setCurrent = function(word) { + sbp.setCurrent(word); + }; + this.getCurrent = function() { + return sbp.getCurrent(); + }; + + function r_mark_regions() { + I_p1 = sbp.limit; + I_p2 = I_p1; + if (!habr1()) { + I_p1 = sbp.cursor; + if (!habr1()) + I_p2 = sbp.cursor; + } + } + + function habr1() { + var v_1; + while (true) { + v_1 = sbp.cursor; + if (sbp.in_grouping(g_V1, 97, 246)) + break; + sbp.cursor = v_1; + if (v_1 >= sbp.limit) + return true; + sbp.cursor++; + } + sbp.cursor = v_1; + while (!sbp.out_grouping(g_V1, 97, 246)) { + if (sbp.cursor >= sbp.limit) + return true; + sbp.cursor++; + } + return false; + } + + function r_R2() { + return I_p2 <= sbp.cursor; + } + + function r_particle_etc() { + var among_var, v_1; + if (sbp.cursor >= I_p1) { + v_1 = sbp.limit_backward; + sbp.limit_backward = I_p1; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_0, 10); + if (among_var) { + sbp.bra = sbp.cursor; + sbp.limit_backward = v_1; + switch (among_var) { + case 1: + if (!sbp.in_grouping_b(g_particle_end, 97, 246)) + return; + break; + case 2: + if (!r_R2()) + return; + break; + } + sbp.slice_del(); + } else + sbp.limit_backward = v_1; + } + } + + function r_possessive() { + var among_var, v_1, v_2; + if (sbp.cursor >= I_p1) { + v_1 = sbp.limit_backward; + sbp.limit_backward = I_p1; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_4, 9); + if (among_var) { + sbp.bra = sbp.cursor; + sbp.limit_backward = v_1; + switch (among_var) { + case 1: + v_2 = sbp.limit - sbp.cursor; + if (!sbp.eq_s_b(1, "k")) { + sbp.cursor = sbp.limit - v_2; + sbp.slice_del(); + } + break; + case 2: + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(3, "kse")) { + sbp.bra = sbp.cursor; + sbp.slice_from("ksi"); + } + break; + case 3: + sbp.slice_del(); + break; + case 4: + if (sbp.find_among_b(a_1, 6)) + sbp.slice_del(); + break; + case 5: + if (sbp.find_among_b(a_2, 6)) + sbp.slice_del(); + break; + case 6: + if (sbp.find_among_b(a_3, 2)) + sbp.slice_del(); + break; + } + } else + sbp.limit_backward = v_1; + } + } + + function r_LONG() { + return sbp.find_among_b(a_5, 7); + } + + function r_VI() { + return sbp.eq_s_b(1, "i") && sbp.in_grouping_b(g_V2, 97, 246); + } + + function r_case_ending() { + var among_var, v_1, v_2; + if (sbp.cursor >= I_p1) { + v_1 = sbp.limit_backward; + sbp.limit_backward = I_p1; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_6, 30); + if (among_var) { + sbp.bra = sbp.cursor; + sbp.limit_backward = v_1; + switch (among_var) { + case 1: + if (!sbp.eq_s_b(1, "a")) + return; + break; + case 2: + case 9: + if (!sbp.eq_s_b(1, "e")) + return; + break; + case 3: + if (!sbp.eq_s_b(1, "i")) + return; + break; + case 4: + if (!sbp.eq_s_b(1, "o")) + return; + break; + case 5: + if (!sbp.eq_s_b(1, "\u00E4")) + return; + break; + case 6: + if (!sbp.eq_s_b(1, "\u00F6")) + return; + break; + case 7: + v_2 = sbp.limit - sbp.cursor; + if (!r_LONG()) { + sbp.cursor = sbp.limit - v_2; + if (!sbp.eq_s_b(2, "ie")) { + sbp.cursor = sbp.limit - v_2; + break; + } + } + sbp.cursor = sbp.limit - v_2; + if (sbp.cursor <= sbp.limit_backward) { + sbp.cursor = sbp.limit - v_2; + break; + } + sbp.cursor--; + sbp.bra = sbp.cursor; + break; + case 8: + if (!sbp.in_grouping_b(g_V1, 97, 246) || + !sbp.out_grouping_b(g_V1, 97, 246)) + return; + break; + } + sbp.slice_del(); + B_ending_removed = true; + } else + sbp.limit_backward = v_1; + } + } + + function r_other_endings() { + var among_var, v_1, v_2; + if (sbp.cursor >= I_p2) { + v_1 = sbp.limit_backward; + sbp.limit_backward = I_p2; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_7, 14); + if (among_var) { + sbp.bra = sbp.cursor; + sbp.limit_backward = v_1; + if (among_var == 1) { + v_2 = sbp.limit - sbp.cursor; + if (sbp.eq_s_b(2, "po")) + return; + sbp.cursor = sbp.limit - v_2; + } + sbp.slice_del(); + } else + sbp.limit_backward = v_1; + } + } + + function r_i_plural() { + var v_1; + if (sbp.cursor >= I_p1) { + v_1 = sbp.limit_backward; + sbp.limit_backward = I_p1; + sbp.ket = sbp.cursor; + if (sbp.find_among_b(a_8, 2)) { + sbp.bra = sbp.cursor; + sbp.limit_backward = v_1; + sbp.slice_del(); + } else + sbp.limit_backward = v_1; + } + } + + function r_t_plural() { + var among_var, v_1, v_2, v_3, v_4, v_5; + if (sbp.cursor >= I_p1) { + v_1 = sbp.limit_backward; + sbp.limit_backward = I_p1; + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(1, "t")) { + sbp.bra = sbp.cursor; + v_2 = sbp.limit - sbp.cursor; + if (sbp.in_grouping_b(g_V1, 97, 246)) { + sbp.cursor = sbp.limit - v_2; + sbp.slice_del(); + sbp.limit_backward = v_1; + v_3 = sbp.limit - sbp.cursor; + if (sbp.cursor >= I_p2) { + sbp.cursor = I_p2; + v_4 = sbp.limit_backward; + sbp.limit_backward = sbp.cursor; + sbp.cursor = sbp.limit - v_3; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_9, 2); + if (among_var) { + sbp.bra = sbp.cursor; + sbp.limit_backward = v_4; + if (among_var == 1) { + v_5 = sbp.limit - sbp.cursor; + if (sbp.eq_s_b(2, "po")) + return; + sbp.cursor = sbp.limit - v_5; + } + sbp.slice_del(); + return; + } + } + } + } + sbp.limit_backward = v_1; + } + } + + function r_tidy() { + var v_1, v_2, v_3, v_4; + if (sbp.cursor >= I_p1) { + v_1 = sbp.limit_backward; + sbp.limit_backward = I_p1; + v_2 = sbp.limit - sbp.cursor; + if (r_LONG()) { + sbp.cursor = sbp.limit - v_2; + sbp.ket = sbp.cursor; + if (sbp.cursor > sbp.limit_backward) { + sbp.cursor--; + sbp.bra = sbp.cursor; + sbp.slice_del(); + } + } + sbp.cursor = sbp.limit - v_2; + sbp.ket = sbp.cursor; + if (sbp.in_grouping_b(g_AEI, 97, 228)) { + sbp.bra = sbp.cursor; + if (sbp.out_grouping_b(g_V1, 97, 246)) + sbp.slice_del(); + } + sbp.cursor = sbp.limit - v_2; + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(1, "j")) { + sbp.bra = sbp.cursor; + v_3 = sbp.limit - sbp.cursor; + if (!sbp.eq_s_b(1, "o")) { + sbp.cursor = sbp.limit - v_3; + if (sbp.eq_s_b(1, "u")) + sbp.slice_del(); + } else + sbp.slice_del(); + } + sbp.cursor = sbp.limit - v_2; + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(1, "o")) { + sbp.bra = sbp.cursor; + if (sbp.eq_s_b(1, "j")) + sbp.slice_del(); + } + sbp.cursor = sbp.limit - v_2; + sbp.limit_backward = v_1; + while (true) { + v_4 = sbp.limit - sbp.cursor; + if (sbp.out_grouping_b(g_V1, 97, 246)) { + sbp.cursor = sbp.limit - v_4; + break; + } + sbp.cursor = sbp.limit - v_4; + if (sbp.cursor <= sbp.limit_backward) + return; + sbp.cursor--; + } + sbp.ket = sbp.cursor; + if (sbp.cursor > sbp.limit_backward) { + sbp.cursor--; + sbp.bra = sbp.cursor; + S_x = sbp.slice_to(); + if (sbp.eq_v_b(S_x)) + sbp.slice_del(); + } + } + } + this.stem = function() { + var v_1 = sbp.cursor; + r_mark_regions(); + B_ending_removed = false; + sbp.limit_backward = v_1; + sbp.cursor = sbp.limit; + r_particle_etc(); + sbp.cursor = sbp.limit; + r_possessive(); + sbp.cursor = sbp.limit; + r_case_ending(); + sbp.cursor = sbp.limit; + r_other_endings(); + sbp.cursor = sbp.limit; + if (B_ending_removed) { + r_i_plural(); + sbp.cursor = sbp.limit; + } else { + sbp.cursor = sbp.limit; + r_t_plural(); + sbp.cursor = sbp.limit; + } + r_tidy(); + return true; + } + }; + + /* and return a function that stems a word for the current locale */ + return function(token) { + // for lunr version 2 + if (typeof token.update === "function") { + return token.update(function(word) { + st.setCurrent(word); + st.stem(); + return st.getCurrent(); + }) + } else { // for lunr version <= 1 + st.setCurrent(token); + st.stem(); + return st.getCurrent(); + } + } + })(); + + lunr.Pipeline.registerFunction(lunr.fi.stemmer, 'stemmer-fi'); + + lunr.fi.stopWordFilter = lunr.generateStopWordFilter('ei eivät emme en et ette että he heidän heidät heihin heille heillä heiltä heissä heistä heitä hän häneen hänelle hänellä häneltä hänen hänessä hänestä hänet häntä itse ja johon joiden joihin joiksi joilla joille joilta joina joissa joista joita joka joksi jolla jolle jolta jona jonka jos jossa josta jota jotka kanssa keiden keihin keiksi keille keillä keiltä keinä keissä keistä keitä keneen keneksi kenelle kenellä keneltä kenen kenenä kenessä kenestä kenet ketkä ketkä ketä koska kuin kuka kun me meidän meidät meihin meille meillä meiltä meissä meistä meitä mihin miksi mikä mille millä miltä minkä minkä minua minulla minulle minulta minun minussa minusta minut minuun minä minä missä mistä mitkä mitä mukaan mutta ne niiden niihin niiksi niille niillä niiltä niin niin niinä niissä niistä niitä noiden noihin noiksi noilla noille noilta noin noina noissa noista noita nuo nyt näiden näihin näiksi näille näillä näiltä näinä näissä näistä näitä nämä ole olemme olen olet olette oli olimme olin olisi olisimme olisin olisit olisitte olisivat olit olitte olivat olla olleet ollut on ovat poikki se sekä sen siihen siinä siitä siksi sille sillä sillä siltä sinua sinulla sinulle sinulta sinun sinussa sinusta sinut sinuun sinä sinä sitä tai te teidän teidät teihin teille teillä teiltä teissä teistä teitä tuo tuohon tuoksi tuolla tuolle tuolta tuon tuona tuossa tuosta tuota tähän täksi tälle tällä tältä tämä tämän tänä tässä tästä tätä vaan vai vaikka yli'.split(' ')); + + lunr.Pipeline.registerFunction(lunr.fi.stopWordFilter, 'stopWordFilter-fi'); + }; +})) \ No newline at end of file diff --git a/mkdocs/contrib/search/lunr-language/lunr.fr.js b/mkdocs/contrib/search/lunr-language/lunr.fr.js new file mode 100644 index 0000000000..2de6d469b3 --- /dev/null +++ b/mkdocs/contrib/search/lunr-language/lunr.fr.js @@ -0,0 +1,703 @@ +/*! + * Lunr languages, `French` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +/** + * 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 environments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + factory()(root.lunr); + } +}(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 function(lunr) { + /* throw error if lunr is not yet included */ + if ('undefined' === typeof lunr) { + throw new Error('Lunr is not present. Please include / require Lunr before this script.'); + } + + /* throw error if lunr stemmer support is not yet included */ + if ('undefined' === typeof lunr.stemmerSupport) { + throw new Error('Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.'); + } + + /* register specific locale function */ + lunr.fr = function() { + this.pipeline.reset(); + this.pipeline.add( + lunr.fr.trimmer, + lunr.fr.stopWordFilter, + lunr.fr.stemmer + ); + + // for lunr version 2 + // this is necessary so that every searched word is also stemmed before + // in lunr <= 1 this is not needed, as it is done using the normal pipeline + if (this.searchPipeline) { + this.searchPipeline.reset(); + this.searchPipeline.add(lunr.fr.stemmer) + } + }; + + /* lunr trimmer function */ + lunr.fr.wordCharacters = "A-Za-z\xAA\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02B8\u02E0-\u02E4\u1D00-\u1D25\u1D2C-\u1D5C\u1D62-\u1D65\u1D6B-\u1D77\u1D79-\u1DBE\u1E00-\u1EFF\u2071\u207F\u2090-\u209C\u212A\u212B\u2132\u214E\u2160-\u2188\u2C60-\u2C7F\uA722-\uA787\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA7FF\uAB30-\uAB5A\uAB5C-\uAB64\uFB00-\uFB06\uFF21-\uFF3A\uFF41-\uFF5A"; + lunr.fr.trimmer = lunr.trimmerSupport.generateTrimmer(lunr.fr.wordCharacters); + + lunr.Pipeline.registerFunction(lunr.fr.trimmer, 'trimmer-fr'); + + /* lunr stemmer function */ + lunr.fr.stemmer = (function() { + /* create the wrapped stemmer object */ + var Among = lunr.stemmerSupport.Among, + SnowballProgram = lunr.stemmerSupport.SnowballProgram, + st = new function FrenchStemmer() { + var a_0 = [new Among("col", -1, -1), new Among("par", -1, -1), + new Among("tap", -1, -1) + ], + a_1 = [new Among("", -1, 4), + new Among("I", 0, 1), new Among("U", 0, 2), new Among("Y", 0, 3) + ], + a_2 = [ + new Among("iqU", -1, 3), new Among("abl", -1, 3), + new Among("I\u00E8r", -1, 4), new Among("i\u00E8r", -1, 4), + new Among("eus", -1, 2), new Among("iv", -1, 1) + ], + a_3 = [ + new Among("ic", -1, 2), new Among("abil", -1, 1), + new Among("iv", -1, 3) + ], + a_4 = [new Among("iqUe", -1, 1), + new Among("atrice", -1, 2), new Among("ance", -1, 1), + new Among("ence", -1, 5), new Among("logie", -1, 3), + new Among("able", -1, 1), new Among("isme", -1, 1), + new Among("euse", -1, 11), new Among("iste", -1, 1), + new Among("ive", -1, 8), new Among("if", -1, 8), + new Among("usion", -1, 4), new Among("ation", -1, 2), + new Among("ution", -1, 4), new Among("ateur", -1, 2), + new Among("iqUes", -1, 1), new Among("atrices", -1, 2), + new Among("ances", -1, 1), new Among("ences", -1, 5), + new Among("logies", -1, 3), new Among("ables", -1, 1), + new Among("ismes", -1, 1), new Among("euses", -1, 11), + new Among("istes", -1, 1), new Among("ives", -1, 8), + new Among("ifs", -1, 8), new Among("usions", -1, 4), + new Among("ations", -1, 2), new Among("utions", -1, 4), + new Among("ateurs", -1, 2), new Among("ments", -1, 15), + new Among("ements", 30, 6), new Among("issements", 31, 12), + new Among("it\u00E9s", -1, 7), new Among("ment", -1, 15), + new Among("ement", 34, 6), new Among("issement", 35, 12), + new Among("amment", 34, 13), new Among("emment", 34, 14), + new Among("aux", -1, 10), new Among("eaux", 39, 9), + new Among("eux", -1, 1), new Among("it\u00E9", -1, 7) + ], + a_5 = [ + new Among("ira", -1, 1), new Among("ie", -1, 1), + new Among("isse", -1, 1), new Among("issante", -1, 1), + new Among("i", -1, 1), new Among("irai", 4, 1), + new Among("ir", -1, 1), new Among("iras", -1, 1), + new Among("ies", -1, 1), new Among("\u00EEmes", -1, 1), + new Among("isses", -1, 1), new Among("issantes", -1, 1), + new Among("\u00EEtes", -1, 1), new Among("is", -1, 1), + new Among("irais", 13, 1), new Among("issais", 13, 1), + new Among("irions", -1, 1), new Among("issions", -1, 1), + new Among("irons", -1, 1), new Among("issons", -1, 1), + new Among("issants", -1, 1), new Among("it", -1, 1), + new Among("irait", 21, 1), new Among("issait", 21, 1), + new Among("issant", -1, 1), new Among("iraIent", -1, 1), + new Among("issaIent", -1, 1), new Among("irent", -1, 1), + new Among("issent", -1, 1), new Among("iront", -1, 1), + new Among("\u00EEt", -1, 1), new Among("iriez", -1, 1), + new Among("issiez", -1, 1), new Among("irez", -1, 1), + new Among("issez", -1, 1) + ], + a_6 = [new Among("a", -1, 3), + new Among("era", 0, 2), new Among("asse", -1, 3), + new Among("ante", -1, 3), new Among("\u00E9e", -1, 2), + new Among("ai", -1, 3), new Among("erai", 5, 2), + new Among("er", -1, 2), new Among("as", -1, 3), + new Among("eras", 8, 2), new Among("\u00E2mes", -1, 3), + new Among("asses", -1, 3), new Among("antes", -1, 3), + new Among("\u00E2tes", -1, 3), new Among("\u00E9es", -1, 2), + new Among("ais", -1, 3), new Among("erais", 15, 2), + new Among("ions", -1, 1), new Among("erions", 17, 2), + new Among("assions", 17, 3), new Among("erons", -1, 2), + new Among("ants", -1, 3), new Among("\u00E9s", -1, 2), + new Among("ait", -1, 3), new Among("erait", 23, 2), + new Among("ant", -1, 3), new Among("aIent", -1, 3), + new Among("eraIent", 26, 2), new Among("\u00E8rent", -1, 2), + new Among("assent", -1, 3), new Among("eront", -1, 2), + new Among("\u00E2t", -1, 3), new Among("ez", -1, 2), + new Among("iez", 32, 2), new Among("eriez", 33, 2), + new Among("assiez", 33, 3), new Among("erez", 32, 2), + new Among("\u00E9", -1, 2) + ], + a_7 = [new Among("e", -1, 3), + new Among("I\u00E8re", 0, 2), new Among("i\u00E8re", 0, 2), + new Among("ion", -1, 1), new Among("Ier", -1, 2), + new Among("ier", -1, 2), new Among("\u00EB", -1, 4) + ], + a_8 = [ + new Among("ell", -1, -1), new Among("eill", -1, -1), + new Among("enn", -1, -1), new Among("onn", -1, -1), + new Among("ett", -1, -1) + ], + g_v = [17, 65, 16, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 128, 130, 103, 8, 5 + ], + g_keep_with_s = [1, 65, 20, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128 + ], + I_p2, I_p1, I_pV, sbp = new SnowballProgram(); + this.setCurrent = function(word) { + sbp.setCurrent(word); + }; + this.getCurrent = function() { + return sbp.getCurrent(); + }; + + function habr1(c1, c2, v_1) { + if (sbp.eq_s(1, c1)) { + sbp.ket = sbp.cursor; + if (sbp.in_grouping(g_v, 97, 251)) { + sbp.slice_from(c2); + sbp.cursor = v_1; + return true; + } + } + return false; + } + + function habr2(c1, c2, v_1) { + if (sbp.eq_s(1, c1)) { + sbp.ket = sbp.cursor; + sbp.slice_from(c2); + sbp.cursor = v_1; + return true; + } + return false; + } + + function r_prelude() { + var v_1, v_2; + while (true) { + v_1 = sbp.cursor; + if (sbp.in_grouping(g_v, 97, 251)) { + sbp.bra = sbp.cursor; + v_2 = sbp.cursor; + if (habr1("u", "U", v_1)) + continue; + sbp.cursor = v_2; + if (habr1("i", "I", v_1)) + continue; + sbp.cursor = v_2; + if (habr2("y", "Y", v_1)) + continue; + } + sbp.cursor = v_1; + sbp.bra = v_1; + if (!habr1("y", "Y", v_1)) { + sbp.cursor = v_1; + if (sbp.eq_s(1, "q")) { + sbp.bra = sbp.cursor; + if (habr2("u", "U", v_1)) + continue; + } + sbp.cursor = v_1; + if (v_1 >= sbp.limit) + return; + sbp.cursor++; + } + } + } + + function habr3() { + while (!sbp.in_grouping(g_v, 97, 251)) { + if (sbp.cursor >= sbp.limit) + return true; + sbp.cursor++; + } + while (!sbp.out_grouping(g_v, 97, 251)) { + if (sbp.cursor >= sbp.limit) + return true; + sbp.cursor++; + } + return false; + } + + function r_mark_regions() { + var v_1 = sbp.cursor; + I_pV = sbp.limit; + I_p1 = I_pV; + I_p2 = I_pV; + if (sbp.in_grouping(g_v, 97, 251) && sbp.in_grouping(g_v, 97, 251) && + sbp.cursor < sbp.limit) + sbp.cursor++; + else { + sbp.cursor = v_1; + if (!sbp.find_among(a_0, 3)) { + sbp.cursor = v_1; + do { + if (sbp.cursor >= sbp.limit) { + sbp.cursor = I_pV; + break; + } + sbp.cursor++; + } while (!sbp.in_grouping(g_v, 97, 251)); + } + } + I_pV = sbp.cursor; + sbp.cursor = v_1; + if (!habr3()) { + I_p1 = sbp.cursor; + if (!habr3()) + I_p2 = sbp.cursor; + } + } + + function r_postlude() { + var among_var, v_1; + while (true) { + v_1 = sbp.cursor; + sbp.bra = v_1; + among_var = sbp.find_among(a_1, 4); + if (!among_var) + break; + sbp.ket = sbp.cursor; + switch (among_var) { + case 1: + sbp.slice_from("i"); + break; + case 2: + sbp.slice_from("u"); + break; + case 3: + sbp.slice_from("y"); + break; + case 4: + if (sbp.cursor >= sbp.limit) + return; + sbp.cursor++; + break; + } + } + } + + function r_RV() { + return I_pV <= sbp.cursor; + } + + function r_R1() { + return I_p1 <= sbp.cursor; + } + + function r_R2() { + return I_p2 <= sbp.cursor; + } + + function r_standard_suffix() { + var among_var, v_1; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_4, 43); + if (among_var) { + sbp.bra = sbp.cursor; + switch (among_var) { + case 1: + if (!r_R2()) + return false; + sbp.slice_del(); + break; + case 2: + if (!r_R2()) + return false; + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(2, "ic")) { + sbp.bra = sbp.cursor; + if (!r_R2()) + sbp.slice_from("iqU"); + else + sbp.slice_del(); + } + break; + case 3: + if (!r_R2()) + return false; + sbp.slice_from("log"); + break; + case 4: + if (!r_R2()) + return false; + sbp.slice_from("u"); + break; + case 5: + if (!r_R2()) + return false; + sbp.slice_from("ent"); + break; + case 6: + if (!r_RV()) + return false; + sbp.slice_del(); + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_2, 6); + if (among_var) { + sbp.bra = sbp.cursor; + switch (among_var) { + case 1: + if (r_R2()) { + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(2, "at")) { + sbp.bra = sbp.cursor; + if (r_R2()) + sbp.slice_del(); + } + } + break; + case 2: + if (r_R2()) + sbp.slice_del(); + else if (r_R1()) + sbp.slice_from("eux"); + break; + case 3: + if (r_R2()) + sbp.slice_del(); + break; + case 4: + if (r_RV()) + sbp.slice_from("i"); + break; + } + } + break; + case 7: + if (!r_R2()) + return false; + sbp.slice_del(); + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_3, 3); + if (among_var) { + sbp.bra = sbp.cursor; + switch (among_var) { + case 1: + if (r_R2()) + sbp.slice_del(); + else + sbp.slice_from("abl"); + break; + case 2: + if (r_R2()) + sbp.slice_del(); + else + sbp.slice_from("iqU"); + break; + case 3: + if (r_R2()) + sbp.slice_del(); + break; + } + } + break; + case 8: + if (!r_R2()) + return false; + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(2, "at")) { + sbp.bra = sbp.cursor; + if (r_R2()) { + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(2, "ic")) { + sbp.bra = sbp.cursor; + if (r_R2()) + sbp.slice_del(); + else + sbp.slice_from("iqU"); + break; + } + } + } + break; + case 9: + sbp.slice_from("eau"); + break; + case 10: + if (!r_R1()) + return false; + sbp.slice_from("al"); + break; + case 11: + if (r_R2()) + sbp.slice_del(); + else if (!r_R1()) + return false; + else + sbp.slice_from("eux"); + break; + case 12: + if (!r_R1() || !sbp.out_grouping_b(g_v, 97, 251)) + return false; + sbp.slice_del(); + break; + case 13: + if (r_RV()) + sbp.slice_from("ant"); + return false; + case 14: + if (r_RV()) + sbp.slice_from("ent"); + return false; + case 15: + v_1 = sbp.limit - sbp.cursor; + if (sbp.in_grouping_b(g_v, 97, 251) && r_RV()) { + sbp.cursor = sbp.limit - v_1; + sbp.slice_del(); + } + return false; + } + return true; + } + return false; + } + + function r_i_verb_suffix() { + var among_var, v_1; + if (sbp.cursor < I_pV) + return false; + v_1 = sbp.limit_backward; + sbp.limit_backward = I_pV; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_5, 35); + if (!among_var) { + sbp.limit_backward = v_1; + return false; + } + sbp.bra = sbp.cursor; + if (among_var == 1) { + if (!sbp.out_grouping_b(g_v, 97, 251)) { + sbp.limit_backward = v_1; + return false; + } + sbp.slice_del(); + } + sbp.limit_backward = v_1; + return true; + } + + function r_verb_suffix() { + var among_var, v_2, v_3; + if (sbp.cursor < I_pV) + return false; + v_2 = sbp.limit_backward; + sbp.limit_backward = I_pV; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_6, 38); + if (!among_var) { + sbp.limit_backward = v_2; + return false; + } + sbp.bra = sbp.cursor; + switch (among_var) { + case 1: + if (!r_R2()) { + sbp.limit_backward = v_2; + return false; + } + sbp.slice_del(); + break; + case 2: + sbp.slice_del(); + break; + case 3: + sbp.slice_del(); + v_3 = sbp.limit - sbp.cursor; + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(1, "e")) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + } else + sbp.cursor = sbp.limit - v_3; + break; + } + sbp.limit_backward = v_2; + return true; + } + + function r_residual_suffix() { + var among_var, v_1 = sbp.limit - sbp.cursor, + v_2, v_4, v_5; + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(1, "s")) { + sbp.bra = sbp.cursor; + v_2 = sbp.limit - sbp.cursor; + if (sbp.out_grouping_b(g_keep_with_s, 97, 232)) { + sbp.cursor = sbp.limit - v_2; + sbp.slice_del(); + } else + sbp.cursor = sbp.limit - v_1; + } else + sbp.cursor = sbp.limit - v_1; + if (sbp.cursor >= I_pV) { + v_4 = sbp.limit_backward; + sbp.limit_backward = I_pV; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_7, 7); + if (among_var) { + sbp.bra = sbp.cursor; + switch (among_var) { + case 1: + if (r_R2()) { + v_5 = sbp.limit - sbp.cursor; + if (!sbp.eq_s_b(1, "s")) { + sbp.cursor = sbp.limit - v_5; + if (!sbp.eq_s_b(1, "t")) + break; + } + sbp.slice_del(); + } + break; + case 2: + sbp.slice_from("i"); + break; + case 3: + sbp.slice_del(); + break; + case 4: + if (sbp.eq_s_b(2, "gu")) + sbp.slice_del(); + break; + } + } + sbp.limit_backward = v_4; + } + } + + function r_un_double() { + var v_1 = sbp.limit - sbp.cursor; + if (sbp.find_among_b(a_8, 5)) { + sbp.cursor = sbp.limit - v_1; + sbp.ket = sbp.cursor; + if (sbp.cursor > sbp.limit_backward) { + sbp.cursor--; + sbp.bra = sbp.cursor; + sbp.slice_del(); + } + } + } + + function r_un_accent() { + var v_1, v_2 = 1; + while (sbp.out_grouping_b(g_v, 97, 251)) + v_2--; + if (v_2 <= 0) { + sbp.ket = sbp.cursor; + v_1 = sbp.limit - sbp.cursor; + if (!sbp.eq_s_b(1, "\u00E9")) { + sbp.cursor = sbp.limit - v_1; + if (!sbp.eq_s_b(1, "\u00E8")) + return; + } + sbp.bra = sbp.cursor; + sbp.slice_from("e"); + } + } + + function habr5() { + if (!r_standard_suffix()) { + sbp.cursor = sbp.limit; + if (!r_i_verb_suffix()) { + sbp.cursor = sbp.limit; + if (!r_verb_suffix()) { + sbp.cursor = sbp.limit; + r_residual_suffix(); + return; + } + } + } + sbp.cursor = sbp.limit; + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(1, "Y")) { + sbp.bra = sbp.cursor; + sbp.slice_from("i"); + } else { + sbp.cursor = sbp.limit; + if (sbp.eq_s_b(1, "\u00E7")) { + sbp.bra = sbp.cursor; + sbp.slice_from("c"); + } + } + } + this.stem = function() { + var v_1 = sbp.cursor; + r_prelude(); + sbp.cursor = v_1; + r_mark_regions(); + sbp.limit_backward = v_1; + sbp.cursor = sbp.limit; + habr5(); + sbp.cursor = sbp.limit; + r_un_double(); + sbp.cursor = sbp.limit; + r_un_accent(); + sbp.cursor = sbp.limit_backward; + r_postlude(); + return true; + } + }; + + /* and return a function that stems a word for the current locale */ + return function(token) { + // for lunr version 2 + if (typeof token.update === "function") { + return token.update(function(word) { + st.setCurrent(word); + st.stem(); + return st.getCurrent(); + }) + } else { // for lunr version <= 1 + st.setCurrent(token); + st.stem(); + return st.getCurrent(); + } + } + })(); + + lunr.Pipeline.registerFunction(lunr.fr.stemmer, 'stemmer-fr'); + + lunr.fr.stopWordFilter = lunr.generateStopWordFilter('ai aie aient aies ait as au aura aurai auraient aurais aurait auras aurez auriez aurions aurons auront aux avaient avais avait avec avez aviez avions avons ayant ayez ayons c ce ceci celà ces cet cette d dans de des du elle en es est et eu eue eues eurent eus eusse eussent eusses eussiez eussions eut eux eûmes eût eûtes furent fus fusse fussent fusses fussiez fussions fut fûmes fût fûtes ici il ils j je l la le les leur leurs lui m ma mais me mes moi mon même n ne nos notre nous on ont ou par pas pour qu que quel quelle quelles quels qui s sa sans se sera serai seraient serais serait seras serez seriez serions serons seront ses soi soient sois soit sommes son sont soyez soyons suis sur t ta te tes toi ton tu un une vos votre vous y à étaient étais était étant étiez étions été étée étées étés êtes'.split(' ')); + + lunr.Pipeline.registerFunction(lunr.fr.stopWordFilter, 'stopWordFilter-fr'); + }; +})) \ No newline at end of file diff --git a/mkdocs/contrib/search/lunr-language/lunr.hu.js b/mkdocs/contrib/search/lunr-language/lunr.hu.js new file mode 100644 index 0000000000..c52219ffee --- /dev/null +++ b/mkdocs/contrib/search/lunr-language/lunr.hu.js @@ -0,0 +1,565 @@ +/*! + * Lunr languages, `Hungarian` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +/** + * 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 environments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + factory()(root.lunr); + } +}(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 function(lunr) { + /* throw error if lunr is not yet included */ + if ('undefined' === typeof lunr) { + throw new Error('Lunr is not present. Please include / require Lunr before this script.'); + } + + /* throw error if lunr stemmer support is not yet included */ + if ('undefined' === typeof lunr.stemmerSupport) { + throw new Error('Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.'); + } + + /* register specific locale function */ + lunr.hu = function() { + this.pipeline.reset(); + this.pipeline.add( + lunr.hu.trimmer, + lunr.hu.stopWordFilter, + lunr.hu.stemmer + ); + + // for lunr version 2 + // this is necessary so that every searched word is also stemmed before + // in lunr <= 1 this is not needed, as it is done using the normal pipeline + if (this.searchPipeline) { + this.searchPipeline.reset(); + this.searchPipeline.add(lunr.hu.stemmer) + } + }; + + /* lunr trimmer function */ + lunr.hu.wordCharacters = "A-Za-z\xAA\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02B8\u02E0-\u02E4\u1D00-\u1D25\u1D2C-\u1D5C\u1D62-\u1D65\u1D6B-\u1D77\u1D79-\u1DBE\u1E00-\u1EFF\u2071\u207F\u2090-\u209C\u212A\u212B\u2132\u214E\u2160-\u2188\u2C60-\u2C7F\uA722-\uA787\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA7FF\uAB30-\uAB5A\uAB5C-\uAB64\uFB00-\uFB06\uFF21-\uFF3A\uFF41-\uFF5A"; + lunr.hu.trimmer = lunr.trimmerSupport.generateTrimmer(lunr.hu.wordCharacters); + + lunr.Pipeline.registerFunction(lunr.hu.trimmer, 'trimmer-hu'); + + /* lunr stemmer function */ + lunr.hu.stemmer = (function() { + /* create the wrapped stemmer object */ + var Among = lunr.stemmerSupport.Among, + SnowballProgram = lunr.stemmerSupport.SnowballProgram, + st = new function HungarianStemmer() { + var a_0 = [new Among("cs", -1, -1), new Among("dzs", -1, -1), + new Among("gy", -1, -1), new Among("ly", -1, -1), + new Among("ny", -1, -1), new Among("sz", -1, -1), + new Among("ty", -1, -1), new Among("zs", -1, -1) + ], + a_1 = [ + new Among("\u00E1", -1, 1), new Among("\u00E9", -1, 2) + ], + a_2 = [ + new Among("bb", -1, -1), new Among("cc", -1, -1), + new Among("dd", -1, -1), new Among("ff", -1, -1), + new Among("gg", -1, -1), new Among("jj", -1, -1), + new Among("kk", -1, -1), new Among("ll", -1, -1), + new Among("mm", -1, -1), new Among("nn", -1, -1), + new Among("pp", -1, -1), new Among("rr", -1, -1), + new Among("ccs", -1, -1), new Among("ss", -1, -1), + new Among("zzs", -1, -1), new Among("tt", -1, -1), + new Among("vv", -1, -1), new Among("ggy", -1, -1), + new Among("lly", -1, -1), new Among("nny", -1, -1), + new Among("tty", -1, -1), new Among("ssz", -1, -1), + new Among("zz", -1, -1) + ], + a_3 = [new Among("al", -1, 1), + new Among("el", -1, 2) + ], + a_4 = [new Among("ba", -1, -1), + new Among("ra", -1, -1), new Among("be", -1, -1), + new Among("re", -1, -1), new Among("ig", -1, -1), + new Among("nak", -1, -1), new Among("nek", -1, -1), + new Among("val", -1, -1), new Among("vel", -1, -1), + new Among("ul", -1, -1), new Among("n\u00E1l", -1, -1), + new Among("n\u00E9l", -1, -1), new Among("b\u00F3l", -1, -1), + new Among("r\u00F3l", -1, -1), new Among("t\u00F3l", -1, -1), + new Among("b\u00F5l", -1, -1), new Among("r\u00F5l", -1, -1), + new Among("t\u00F5l", -1, -1), new Among("\u00FCl", -1, -1), + new Among("n", -1, -1), new Among("an", 19, -1), + new Among("ban", 20, -1), new Among("en", 19, -1), + new Among("ben", 22, -1), new Among("k\u00E9ppen", 22, -1), + new Among("on", 19, -1), new Among("\u00F6n", 19, -1), + new Among("k\u00E9pp", -1, -1), new Among("kor", -1, -1), + new Among("t", -1, -1), new Among("at", 29, -1), + new Among("et", 29, -1), new Among("k\u00E9nt", 29, -1), + new Among("ank\u00E9nt", 32, -1), new Among("enk\u00E9nt", 32, -1), + new Among("onk\u00E9nt", 32, -1), new Among("ot", 29, -1), + new Among("\u00E9rt", 29, -1), new Among("\u00F6t", 29, -1), + new Among("hez", -1, -1), new Among("hoz", -1, -1), + new Among("h\u00F6z", -1, -1), new Among("v\u00E1", -1, -1), + new Among("v\u00E9", -1, -1) + ], + a_5 = [new Among("\u00E1n", -1, 2), + new Among("\u00E9n", -1, 1), new Among("\u00E1nk\u00E9nt", -1, 3) + ], + a_6 = [ + new Among("stul", -1, 2), new Among("astul", 0, 1), + new Among("\u00E1stul", 0, 3), new Among("st\u00FCl", -1, 2), + new Among("est\u00FCl", 3, 1), new Among("\u00E9st\u00FCl", 3, 4) + ], + a_7 = [ + new Among("\u00E1", -1, 1), new Among("\u00E9", -1, 2) + ], + a_8 = [ + new Among("k", -1, 7), new Among("ak", 0, 4), + new Among("ek", 0, 6), new Among("ok", 0, 5), + new Among("\u00E1k", 0, 1), new Among("\u00E9k", 0, 2), + new Among("\u00F6k", 0, 3) + ], + a_9 = [new Among("\u00E9i", -1, 7), + new Among("\u00E1\u00E9i", 0, 6), new Among("\u00E9\u00E9i", 0, 5), + new Among("\u00E9", -1, 9), new Among("k\u00E9", 3, 4), + new Among("ak\u00E9", 4, 1), new Among("ek\u00E9", 4, 1), + new Among("ok\u00E9", 4, 1), new Among("\u00E1k\u00E9", 4, 3), + new Among("\u00E9k\u00E9", 4, 2), new Among("\u00F6k\u00E9", 4, 1), + new Among("\u00E9\u00E9", 3, 8) + ], + a_10 = [new Among("a", -1, 18), + new Among("ja", 0, 17), new Among("d", -1, 16), + new Among("ad", 2, 13), new Among("ed", 2, 13), + new Among("od", 2, 13), new Among("\u00E1d", 2, 14), + new Among("\u00E9d", 2, 15), new Among("\u00F6d", 2, 13), + new Among("e", -1, 18), new Among("je", 9, 17), + new Among("nk", -1, 4), new Among("unk", 11, 1), + new Among("\u00E1nk", 11, 2), new Among("\u00E9nk", 11, 3), + new Among("\u00FCnk", 11, 1), new Among("uk", -1, 8), + new Among("juk", 16, 7), new Among("\u00E1juk", 17, 5), + new Among("\u00FCk", -1, 8), new Among("j\u00FCk", 19, 7), + new Among("\u00E9j\u00FCk", 20, 6), new Among("m", -1, 12), + new Among("am", 22, 9), new Among("em", 22, 9), + new Among("om", 22, 9), new Among("\u00E1m", 22, 10), + new Among("\u00E9m", 22, 11), new Among("o", -1, 18), + new Among("\u00E1", -1, 19), new Among("\u00E9", -1, 20) + ], + a_11 = [ + new Among("id", -1, 10), new Among("aid", 0, 9), + new Among("jaid", 1, 6), new Among("eid", 0, 9), + new Among("jeid", 3, 6), new Among("\u00E1id", 0, 7), + new Among("\u00E9id", 0, 8), new Among("i", -1, 15), + new Among("ai", 7, 14), new Among("jai", 8, 11), + new Among("ei", 7, 14), new Among("jei", 10, 11), + new Among("\u00E1i", 7, 12), new Among("\u00E9i", 7, 13), + new Among("itek", -1, 24), new Among("eitek", 14, 21), + new Among("jeitek", 15, 20), new Among("\u00E9itek", 14, 23), + new Among("ik", -1, 29), new Among("aik", 18, 26), + new Among("jaik", 19, 25), new Among("eik", 18, 26), + new Among("jeik", 21, 25), new Among("\u00E1ik", 18, 27), + new Among("\u00E9ik", 18, 28), new Among("ink", -1, 20), + new Among("aink", 25, 17), new Among("jaink", 26, 16), + new Among("eink", 25, 17), new Among("jeink", 28, 16), + new Among("\u00E1ink", 25, 18), new Among("\u00E9ink", 25, 19), + new Among("aitok", -1, 21), new Among("jaitok", 32, 20), + new Among("\u00E1itok", -1, 22), new Among("im", -1, 5), + new Among("aim", 35, 4), new Among("jaim", 36, 1), + new Among("eim", 35, 4), new Among("jeim", 38, 1), + new Among("\u00E1im", 35, 2), new Among("\u00E9im", 35, 3) + ], + g_v = [ + 17, 65, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 17, 52, 14 + ], + I_p1, sbp = new SnowballProgram(); + this.setCurrent = function(word) { + sbp.setCurrent(word); + }; + this.getCurrent = function() { + return sbp.getCurrent(); + }; + + function r_mark_regions() { + var v_1 = sbp.cursor, + v_2; + I_p1 = sbp.limit; + if (sbp.in_grouping(g_v, 97, 252)) { + while (true) { + v_2 = sbp.cursor; + if (sbp.out_grouping(g_v, 97, 252)) { + sbp.cursor = v_2; + if (!sbp.find_among(a_0, 8)) { + sbp.cursor = v_2; + if (v_2 < sbp.limit) + sbp.cursor++; + } + I_p1 = sbp.cursor; + return; + } + sbp.cursor = v_2; + if (v_2 >= sbp.limit) { + I_p1 = v_2; + return; + } + sbp.cursor++; + } + } + sbp.cursor = v_1; + if (sbp.out_grouping(g_v, 97, 252)) { + while (!sbp.in_grouping(g_v, 97, 252)) { + if (sbp.cursor >= sbp.limit) + return; + sbp.cursor++; + } + I_p1 = sbp.cursor; + } + } + + function r_R1() { + return I_p1 <= sbp.cursor; + } + + function r_v_ending() { + var among_var; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_1, 2); + if (among_var) { + sbp.bra = sbp.cursor; + if (r_R1()) { + switch (among_var) { + case 1: + sbp.slice_from("a"); + break; + case 2: + sbp.slice_from("e"); + break; + } + } + } + } + + function r_double() { + var v_1 = sbp.limit - sbp.cursor; + if (!sbp.find_among_b(a_2, 23)) + return false; + sbp.cursor = sbp.limit - v_1; + return true; + } + + function r_undouble() { + if (sbp.cursor > sbp.limit_backward) { + sbp.cursor--; + sbp.ket = sbp.cursor; + var c = sbp.cursor - 1; + if (sbp.limit_backward <= c && c <= sbp.limit) { + sbp.cursor = c; + sbp.bra = c; + sbp.slice_del(); + } + } + } + + function r_instrum() { + var among_var; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_3, 2); + if (among_var) { + sbp.bra = sbp.cursor; + if (r_R1()) { + if (among_var == 1 || among_var == 2) + if (!r_double()) + return; + sbp.slice_del(); + r_undouble(); + } + } + } + + function r_case() { + sbp.ket = sbp.cursor; + if (sbp.find_among_b(a_4, 44)) { + sbp.bra = sbp.cursor; + if (r_R1()) { + sbp.slice_del(); + r_v_ending(); + } + } + } + + function r_case_special() { + var among_var; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_5, 3); + if (among_var) { + sbp.bra = sbp.cursor; + if (r_R1()) { + switch (among_var) { + case 1: + sbp.slice_from("e"); + break; + case 2: + case 3: + sbp.slice_from("a"); + break; + } + } + } + } + + function r_case_other() { + var among_var; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_6, 6); + if (among_var) { + sbp.bra = sbp.cursor; + if (r_R1()) { + switch (among_var) { + case 1: + case 2: + sbp.slice_del(); + break; + case 3: + sbp.slice_from("a"); + break; + case 4: + sbp.slice_from("e"); + break; + } + } + } + } + + function r_factive() { + var among_var; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_7, 2); + if (among_var) { + sbp.bra = sbp.cursor; + if (r_R1()) { + if (among_var == 1 || among_var == 2) + if (!r_double()) + return; + sbp.slice_del(); + r_undouble() + } + } + } + + function r_plural() { + var among_var; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_8, 7); + if (among_var) { + sbp.bra = sbp.cursor; + if (r_R1()) { + switch (among_var) { + case 1: + sbp.slice_from("a"); + break; + case 2: + sbp.slice_from("e"); + break; + case 3: + case 4: + case 5: + case 6: + case 7: + sbp.slice_del(); + break; + } + } + } + } + + function r_owned() { + var among_var; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_9, 12); + if (among_var) { + sbp.bra = sbp.cursor; + if (r_R1()) { + switch (among_var) { + case 1: + case 4: + case 7: + case 9: + sbp.slice_del(); + break; + case 2: + case 5: + case 8: + sbp.slice_from("e"); + break; + case 3: + case 6: + sbp.slice_from("a"); + break; + } + } + } + } + + function r_sing_owner() { + var among_var; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_10, 31); + if (among_var) { + sbp.bra = sbp.cursor; + if (r_R1()) { + switch (among_var) { + case 1: + case 4: + case 7: + case 8: + case 9: + case 12: + case 13: + case 16: + case 17: + case 18: + sbp.slice_del(); + break; + case 2: + case 5: + case 10: + case 14: + case 19: + sbp.slice_from("a"); + break; + case 3: + case 6: + case 11: + case 15: + case 20: + sbp.slice_from("e"); + break; + } + } + } + } + + function r_plur_owner() { + var among_var; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_11, 42); + if (among_var) { + sbp.bra = sbp.cursor; + if (r_R1()) { + switch (among_var) { + case 1: + case 4: + case 5: + case 6: + case 9: + case 10: + case 11: + case 14: + case 15: + case 16: + case 17: + case 20: + case 21: + case 24: + case 25: + case 26: + case 29: + sbp.slice_del(); + break; + case 2: + case 7: + case 12: + case 18: + case 22: + case 27: + sbp.slice_from("a"); + break; + case 3: + case 8: + case 13: + case 19: + case 23: + case 28: + sbp.slice_from("e"); + break; + } + } + } + } + this.stem = function() { + var v_1 = sbp.cursor; + r_mark_regions(); + sbp.limit_backward = v_1; + sbp.cursor = sbp.limit; + r_instrum(); + sbp.cursor = sbp.limit; + r_case(); + sbp.cursor = sbp.limit; + r_case_special(); + sbp.cursor = sbp.limit; + r_case_other(); + sbp.cursor = sbp.limit; + r_factive(); + sbp.cursor = sbp.limit; + r_owned(); + sbp.cursor = sbp.limit; + r_sing_owner(); + sbp.cursor = sbp.limit; + r_plur_owner(); + sbp.cursor = sbp.limit; + r_plural(); + return true; + } + }; + + /* and return a function that stems a word for the current locale */ + return function(token) { + // for lunr version 2 + if (typeof token.update === "function") { + return token.update(function(word) { + st.setCurrent(word); + st.stem(); + return st.getCurrent(); + }) + } else { // for lunr version <= 1 + st.setCurrent(token); + st.stem(); + return st.getCurrent(); + } + } + })(); + + lunr.Pipeline.registerFunction(lunr.hu.stemmer, 'stemmer-hu'); + + lunr.hu.stopWordFilter = lunr.generateStopWordFilter('a abban ahhoz ahogy ahol aki akik akkor alatt amely amelyek amelyekben amelyeket amelyet amelynek ami amikor amit amolyan amíg annak arra arról az azok azon azonban azt aztán azután azzal azért be belül benne bár cikk cikkek cikkeket csak de e ebben eddig egy egyes egyetlen egyik egyre egyéb egész ehhez ekkor el ellen elsõ elég elõ elõször elõtt emilyen ennek erre ez ezek ezen ezt ezzel ezért fel felé hanem hiszen hogy hogyan igen ill ill. illetve ilyen ilyenkor ismét ison itt jobban jó jól kell kellett keressünk keresztül ki kívül között közül legalább legyen lehet lehetett lenne lenni lesz lett maga magát majd majd meg mellett mely melyek mert mi mikor milyen minden mindenki mindent mindig mint mintha mit mivel miért most már más másik még míg nagy nagyobb nagyon ne nekem neki nem nincs néha néhány nélkül olyan ott pedig persze rá s saját sem semmi sok sokat sokkal szemben szerint szinte számára talán tehát teljes tovább továbbá több ugyanis utolsó után utána vagy vagyis vagyok valaki valami valamint való van vannak vele vissza viszont volna volt voltak voltam voltunk által általában át én éppen és így õ õk õket össze úgy új újabb újra'.split(' ')); + + lunr.Pipeline.registerFunction(lunr.hu.stopWordFilter, 'stopWordFilter-hu'); + }; +})) \ No newline at end of file diff --git a/mkdocs/contrib/search/lunr-language/lunr.it.js b/mkdocs/contrib/search/lunr-language/lunr.it.js new file mode 100644 index 0000000000..676863b563 --- /dev/null +++ b/mkdocs/contrib/search/lunr-language/lunr.it.js @@ -0,0 +1,617 @@ +/*! + * Lunr languages, `Italian` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +/** + * 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 environments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + factory()(root.lunr); + } +}(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 function(lunr) { + /* throw error if lunr is not yet included */ + if ('undefined' === typeof lunr) { + throw new Error('Lunr is not present. Please include / require Lunr before this script.'); + } + + /* throw error if lunr stemmer support is not yet included */ + if ('undefined' === typeof lunr.stemmerSupport) { + throw new Error('Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.'); + } + + /* register specific locale function */ + lunr.it = function() { + this.pipeline.reset(); + this.pipeline.add( + lunr.it.trimmer, + lunr.it.stopWordFilter, + lunr.it.stemmer + ); + + // for lunr version 2 + // this is necessary so that every searched word is also stemmed before + // in lunr <= 1 this is not needed, as it is done using the normal pipeline + if (this.searchPipeline) { + this.searchPipeline.reset(); + this.searchPipeline.add(lunr.it.stemmer) + } + }; + + /* lunr trimmer function */ + lunr.it.wordCharacters = "A-Za-z\xAA\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02B8\u02E0-\u02E4\u1D00-\u1D25\u1D2C-\u1D5C\u1D62-\u1D65\u1D6B-\u1D77\u1D79-\u1DBE\u1E00-\u1EFF\u2071\u207F\u2090-\u209C\u212A\u212B\u2132\u214E\u2160-\u2188\u2C60-\u2C7F\uA722-\uA787\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA7FF\uAB30-\uAB5A\uAB5C-\uAB64\uFB00-\uFB06\uFF21-\uFF3A\uFF41-\uFF5A"; + lunr.it.trimmer = lunr.trimmerSupport.generateTrimmer(lunr.it.wordCharacters); + + lunr.Pipeline.registerFunction(lunr.it.trimmer, 'trimmer-it'); + + /* lunr stemmer function */ + lunr.it.stemmer = (function() { + /* create the wrapped stemmer object */ + var Among = lunr.stemmerSupport.Among, + SnowballProgram = lunr.stemmerSupport.SnowballProgram, + st = new function ItalianStemmer() { + var a_0 = [new Among("", -1, 7), new Among("qu", 0, 6), + new Among("\u00E1", 0, 1), new Among("\u00E9", 0, 2), + new Among("\u00ED", 0, 3), new Among("\u00F3", 0, 4), + new Among("\u00FA", 0, 5) + ], + a_1 = [new Among("", -1, 3), + new Among("I", 0, 1), new Among("U", 0, 2) + ], + a_2 = [ + new Among("la", -1, -1), new Among("cela", 0, -1), + new Among("gliela", 0, -1), new Among("mela", 0, -1), + new Among("tela", 0, -1), new Among("vela", 0, -1), + new Among("le", -1, -1), new Among("cele", 6, -1), + new Among("gliele", 6, -1), new Among("mele", 6, -1), + new Among("tele", 6, -1), new Among("vele", 6, -1), + new Among("ne", -1, -1), new Among("cene", 12, -1), + new Among("gliene", 12, -1), new Among("mene", 12, -1), + new Among("sene", 12, -1), new Among("tene", 12, -1), + new Among("vene", 12, -1), new Among("ci", -1, -1), + new Among("li", -1, -1), new Among("celi", 20, -1), + new Among("glieli", 20, -1), new Among("meli", 20, -1), + new Among("teli", 20, -1), new Among("veli", 20, -1), + new Among("gli", 20, -1), new Among("mi", -1, -1), + new Among("si", -1, -1), new Among("ti", -1, -1), + new Among("vi", -1, -1), new Among("lo", -1, -1), + new Among("celo", 31, -1), new Among("glielo", 31, -1), + new Among("melo", 31, -1), new Among("telo", 31, -1), + new Among("velo", 31, -1) + ], + a_3 = [new Among("ando", -1, 1), + new Among("endo", -1, 1), new Among("ar", -1, 2), + new Among("er", -1, 2), new Among("ir", -1, 2) + ], + a_4 = [ + new Among("ic", -1, -1), new Among("abil", -1, -1), + new Among("os", -1, -1), new Among("iv", -1, 1) + ], + a_5 = [ + new Among("ic", -1, 1), new Among("abil", -1, 1), + new Among("iv", -1, 1) + ], + a_6 = [new Among("ica", -1, 1), + new Among("logia", -1, 3), new Among("osa", -1, 1), + new Among("ista", -1, 1), new Among("iva", -1, 9), + new Among("anza", -1, 1), new Among("enza", -1, 5), + new Among("ice", -1, 1), new Among("atrice", 7, 1), + new Among("iche", -1, 1), new Among("logie", -1, 3), + new Among("abile", -1, 1), new Among("ibile", -1, 1), + new Among("usione", -1, 4), new Among("azione", -1, 2), + new Among("uzione", -1, 4), new Among("atore", -1, 2), + new Among("ose", -1, 1), new Among("ante", -1, 1), + new Among("mente", -1, 1), new Among("amente", 19, 7), + new Among("iste", -1, 1), new Among("ive", -1, 9), + new Among("anze", -1, 1), new Among("enze", -1, 5), + new Among("ici", -1, 1), new Among("atrici", 25, 1), + new Among("ichi", -1, 1), new Among("abili", -1, 1), + new Among("ibili", -1, 1), new Among("ismi", -1, 1), + new Among("usioni", -1, 4), new Among("azioni", -1, 2), + new Among("uzioni", -1, 4), new Among("atori", -1, 2), + new Among("osi", -1, 1), new Among("anti", -1, 1), + new Among("amenti", -1, 6), new Among("imenti", -1, 6), + new Among("isti", -1, 1), new Among("ivi", -1, 9), + new Among("ico", -1, 1), new Among("ismo", -1, 1), + new Among("oso", -1, 1), new Among("amento", -1, 6), + new Among("imento", -1, 6), new Among("ivo", -1, 9), + new Among("it\u00E0", -1, 8), new Among("ist\u00E0", -1, 1), + new Among("ist\u00E8", -1, 1), new Among("ist\u00EC", -1, 1) + ], + a_7 = [ + new Among("isca", -1, 1), new Among("enda", -1, 1), + new Among("ata", -1, 1), new Among("ita", -1, 1), + new Among("uta", -1, 1), new Among("ava", -1, 1), + new Among("eva", -1, 1), new Among("iva", -1, 1), + new Among("erebbe", -1, 1), new Among("irebbe", -1, 1), + new Among("isce", -1, 1), new Among("ende", -1, 1), + new Among("are", -1, 1), new Among("ere", -1, 1), + new Among("ire", -1, 1), new Among("asse", -1, 1), + new Among("ate", -1, 1), new Among("avate", 16, 1), + new Among("evate", 16, 1), new Among("ivate", 16, 1), + new Among("ete", -1, 1), new Among("erete", 20, 1), + new Among("irete", 20, 1), new Among("ite", -1, 1), + new Among("ereste", -1, 1), new Among("ireste", -1, 1), + new Among("ute", -1, 1), new Among("erai", -1, 1), + new Among("irai", -1, 1), new Among("isci", -1, 1), + new Among("endi", -1, 1), new Among("erei", -1, 1), + new Among("irei", -1, 1), new Among("assi", -1, 1), + new Among("ati", -1, 1), new Among("iti", -1, 1), + new Among("eresti", -1, 1), new Among("iresti", -1, 1), + new Among("uti", -1, 1), new Among("avi", -1, 1), + new Among("evi", -1, 1), new Among("ivi", -1, 1), + new Among("isco", -1, 1), new Among("ando", -1, 1), + new Among("endo", -1, 1), new Among("Yamo", -1, 1), + new Among("iamo", -1, 1), new Among("avamo", -1, 1), + new Among("evamo", -1, 1), new Among("ivamo", -1, 1), + new Among("eremo", -1, 1), new Among("iremo", -1, 1), + new Among("assimo", -1, 1), new Among("ammo", -1, 1), + new Among("emmo", -1, 1), new Among("eremmo", 54, 1), + new Among("iremmo", 54, 1), new Among("immo", -1, 1), + new Among("ano", -1, 1), new Among("iscano", 58, 1), + new Among("avano", 58, 1), new Among("evano", 58, 1), + new Among("ivano", 58, 1), new Among("eranno", -1, 1), + new Among("iranno", -1, 1), new Among("ono", -1, 1), + new Among("iscono", 65, 1), new Among("arono", 65, 1), + new Among("erono", 65, 1), new Among("irono", 65, 1), + new Among("erebbero", -1, 1), new Among("irebbero", -1, 1), + new Among("assero", -1, 1), new Among("essero", -1, 1), + new Among("issero", -1, 1), new Among("ato", -1, 1), + new Among("ito", -1, 1), new Among("uto", -1, 1), + new Among("avo", -1, 1), new Among("evo", -1, 1), + new Among("ivo", -1, 1), new Among("ar", -1, 1), + new Among("ir", -1, 1), new Among("er\u00E0", -1, 1), + new Among("ir\u00E0", -1, 1), new Among("er\u00F2", -1, 1), + new Among("ir\u00F2", -1, 1) + ], + g_v = [17, 65, 16, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 128, 128, 8, 2, 1 + ], + g_AEIO = [17, 65, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 8, 2 + ], + g_CG = [17], + I_p2, I_p1, I_pV, sbp = new SnowballProgram(); + this.setCurrent = function(word) { + sbp.setCurrent(word); + }; + this.getCurrent = function() { + return sbp.getCurrent(); + }; + + function habr1(c1, c2, v_1) { + if (sbp.eq_s(1, c1)) { + sbp.ket = sbp.cursor; + if (sbp.in_grouping(g_v, 97, 249)) { + sbp.slice_from(c2); + sbp.cursor = v_1; + return true; + } + } + return false; + } + + function r_prelude() { + var among_var, v_1 = sbp.cursor, + v_2, v_3, v_4; + while (true) { + sbp.bra = sbp.cursor; + among_var = sbp.find_among(a_0, 7); + if (among_var) { + sbp.ket = sbp.cursor; + switch (among_var) { + case 1: + sbp.slice_from("\u00E0"); + continue; + case 2: + sbp.slice_from("\u00E8"); + continue; + case 3: + sbp.slice_from("\u00EC"); + continue; + case 4: + sbp.slice_from("\u00F2"); + continue; + case 5: + sbp.slice_from("\u00F9"); + continue; + case 6: + sbp.slice_from("qU"); + continue; + case 7: + if (sbp.cursor >= sbp.limit) + break; + sbp.cursor++; + continue; + } + } + break; + } + sbp.cursor = v_1; + while (true) { + v_2 = sbp.cursor; + while (true) { + v_3 = sbp.cursor; + if (sbp.in_grouping(g_v, 97, 249)) { + sbp.bra = sbp.cursor; + v_4 = sbp.cursor; + if (habr1("u", "U", v_3)) + break; + sbp.cursor = v_4; + if (habr1("i", "I", v_3)) + break; + } + sbp.cursor = v_3; + if (sbp.cursor >= sbp.limit) { + sbp.cursor = v_2; + return; + } + sbp.cursor++; + } + } + } + + function habr2(v_1) { + sbp.cursor = v_1; + if (!sbp.in_grouping(g_v, 97, 249)) + return false; + while (!sbp.out_grouping(g_v, 97, 249)) { + if (sbp.cursor >= sbp.limit) + return false; + sbp.cursor++; + } + return true; + } + + function habr3() { + if (sbp.in_grouping(g_v, 97, 249)) { + var v_1 = sbp.cursor; + if (sbp.out_grouping(g_v, 97, 249)) { + while (!sbp.in_grouping(g_v, 97, 249)) { + if (sbp.cursor >= sbp.limit) + return habr2(v_1); + sbp.cursor++; + } + return true; + } + return habr2(v_1); + } + return false; + } + + function habr4() { + var v_1 = sbp.cursor, + v_2; + if (!habr3()) { + sbp.cursor = v_1; + if (!sbp.out_grouping(g_v, 97, 249)) + return; + v_2 = sbp.cursor; + if (sbp.out_grouping(g_v, 97, 249)) { + while (!sbp.in_grouping(g_v, 97, 249)) { + if (sbp.cursor >= sbp.limit) { + sbp.cursor = v_2; + if (sbp.in_grouping(g_v, 97, 249) && + sbp.cursor < sbp.limit) + sbp.cursor++; + return; + } + sbp.cursor++; + } + I_pV = sbp.cursor; + return; + } + sbp.cursor = v_2; + if (!sbp.in_grouping(g_v, 97, 249) || sbp.cursor >= sbp.limit) + return; + sbp.cursor++; + } + I_pV = sbp.cursor; + } + + function habr5() { + while (!sbp.in_grouping(g_v, 97, 249)) { + if (sbp.cursor >= sbp.limit) + return false; + sbp.cursor++; + } + while (!sbp.out_grouping(g_v, 97, 249)) { + if (sbp.cursor >= sbp.limit) + return false; + sbp.cursor++; + } + return true; + } + + function r_mark_regions() { + var v_1 = sbp.cursor; + I_pV = sbp.limit; + I_p1 = I_pV; + I_p2 = I_pV; + habr4(); + sbp.cursor = v_1; + if (habr5()) { + I_p1 = sbp.cursor; + if (habr5()) + I_p2 = sbp.cursor; + } + } + + function r_postlude() { + var among_var; + while (true) { + sbp.bra = sbp.cursor; + among_var = sbp.find_among(a_1, 3); + if (!among_var) + break; + sbp.ket = sbp.cursor; + switch (among_var) { + case 1: + sbp.slice_from("i"); + break; + case 2: + sbp.slice_from("u"); + break; + case 3: + if (sbp.cursor >= sbp.limit) + return; + sbp.cursor++; + break; + } + } + } + + function r_RV() { + return I_pV <= sbp.cursor; + } + + function r_R1() { + return I_p1 <= sbp.cursor; + } + + function r_R2() { + return I_p2 <= sbp.cursor; + } + + function r_attached_pronoun() { + var among_var; + sbp.ket = sbp.cursor; + if (sbp.find_among_b(a_2, 37)) { + sbp.bra = sbp.cursor; + among_var = sbp.find_among_b(a_3, 5); + if (among_var && r_RV()) { + switch (among_var) { + case 1: + sbp.slice_del(); + break; + case 2: + sbp.slice_from("e"); + break; + } + } + } + } + + function r_standard_suffix() { + var among_var; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_6, 51); + if (!among_var) + return false; + sbp.bra = sbp.cursor; + switch (among_var) { + case 1: + if (!r_R2()) + return false; + sbp.slice_del(); + break; + case 2: + if (!r_R2()) + return false; + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(2, "ic")) { + sbp.bra = sbp.cursor; + if (r_R2()) + sbp.slice_del(); + } + break; + case 3: + if (!r_R2()) + return false; + sbp.slice_from("log"); + break; + case 4: + if (!r_R2()) + return false; + sbp.slice_from("u"); + break; + case 5: + if (!r_R2()) + return false; + sbp.slice_from("ente"); + break; + case 6: + if (!r_RV()) + return false; + sbp.slice_del(); + break; + case 7: + if (!r_R1()) + return false; + sbp.slice_del(); + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_4, 4); + if (among_var) { + sbp.bra = sbp.cursor; + if (r_R2()) { + sbp.slice_del(); + if (among_var == 1) { + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(2, "at")) { + sbp.bra = sbp.cursor; + if (r_R2()) + sbp.slice_del(); + } + } + } + } + break; + case 8: + if (!r_R2()) + return false; + sbp.slice_del(); + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_5, 3); + if (among_var) { + sbp.bra = sbp.cursor; + if (among_var == 1) + if (r_R2()) + sbp.slice_del(); + } + break; + case 9: + if (!r_R2()) + return false; + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(2, "at")) { + sbp.bra = sbp.cursor; + if (r_R2()) { + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(2, "ic")) { + sbp.bra = sbp.cursor; + if (r_R2()) + sbp.slice_del(); + } + } + } + break; + } + return true; + } + + function r_verb_suffix() { + var among_var, v_1; + if (sbp.cursor >= I_pV) { + v_1 = sbp.limit_backward; + sbp.limit_backward = I_pV; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_7, 87); + if (among_var) { + sbp.bra = sbp.cursor; + if (among_var == 1) + sbp.slice_del(); + } + sbp.limit_backward = v_1; + } + } + + function habr6() { + var v_1 = sbp.limit - sbp.cursor; + sbp.ket = sbp.cursor; + if (sbp.in_grouping_b(g_AEIO, 97, 242)) { + sbp.bra = sbp.cursor; + if (r_RV()) { + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(1, "i")) { + sbp.bra = sbp.cursor; + if (r_RV()) { + sbp.slice_del(); + return; + } + } + } + } + sbp.cursor = sbp.limit - v_1; + } + + function r_vowel_suffix() { + habr6(); + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(1, "h")) { + sbp.bra = sbp.cursor; + if (sbp.in_grouping_b(g_CG, 99, 103)) + if (r_RV()) + sbp.slice_del(); + } + } + this.stem = function() { + var v_1 = sbp.cursor; + r_prelude(); + sbp.cursor = v_1; + r_mark_regions(); + sbp.limit_backward = v_1; + sbp.cursor = sbp.limit; + r_attached_pronoun(); + sbp.cursor = sbp.limit; + if (!r_standard_suffix()) { + sbp.cursor = sbp.limit; + r_verb_suffix(); + } + sbp.cursor = sbp.limit; + r_vowel_suffix(); + sbp.cursor = sbp.limit_backward; + r_postlude(); + return true; + } + }; + + /* and return a function that stems a word for the current locale */ + return function(token) { + // for lunr version 2 + if (typeof token.update === "function") { + return token.update(function(word) { + st.setCurrent(word); + st.stem(); + return st.getCurrent(); + }) + } else { // for lunr version <= 1 + st.setCurrent(token); + st.stem(); + return st.getCurrent(); + } + } + })(); + + lunr.Pipeline.registerFunction(lunr.it.stemmer, 'stemmer-it'); + + lunr.it.stopWordFilter = lunr.generateStopWordFilter('a abbia abbiamo abbiano abbiate ad agl agli ai al all alla alle allo anche avemmo avendo avesse avessero avessi avessimo aveste avesti avete aveva avevamo avevano avevate avevi avevo avrai avranno avrebbe avrebbero avrei avremmo avremo avreste avresti avrete avrà avrò avuta avute avuti avuto c che chi ci coi col come con contro cui da dagl dagli dai dal dall dalla dalle dallo degl degli dei del dell della delle dello di dov dove e ebbe ebbero ebbi ed era erano eravamo eravate eri ero essendo faccia facciamo facciano facciate faccio facemmo facendo facesse facessero facessi facessimo faceste facesti faceva facevamo facevano facevate facevi facevo fai fanno farai faranno farebbe farebbero farei faremmo faremo fareste faresti farete farà farò fece fecero feci fosse fossero fossi fossimo foste fosti fu fui fummo furono gli ha hai hanno ho i il in io l la le lei li lo loro lui ma mi mia mie miei mio ne negl negli nei nel nell nella nelle nello noi non nostra nostre nostri nostro o per perché più quale quanta quante quanti quanto quella quelle quelli quello questa queste questi questo sarai saranno sarebbe sarebbero sarei saremmo saremo sareste saresti sarete sarà sarò se sei si sia siamo siano siate siete sono sta stai stando stanno starai staranno starebbe starebbero starei staremmo staremo stareste staresti starete starà starò stava stavamo stavano stavate stavi stavo stemmo stesse stessero stessi stessimo steste stesti stette stettero stetti stia stiamo stiano stiate sto su sua sue sugl sugli sui sul sull sulla sulle sullo suo suoi ti tra tu tua tue tuo tuoi tutti tutto un una uno vi voi vostra vostre vostri vostro è'.split(' ')); + + lunr.Pipeline.registerFunction(lunr.it.stopWordFilter, 'stopWordFilter-it'); + }; +})) \ No newline at end of file diff --git a/mkdocs/contrib/search/lunr-language/lunr.jp.js b/mkdocs/contrib/search/lunr-language/lunr.jp.js new file mode 100644 index 0000000000..aef6e31dd7 --- /dev/null +++ b/mkdocs/contrib/search/lunr-language/lunr.jp.js @@ -0,0 +1,134 @@ +/*! + * Lunr languages, `Japanese` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Chad Liu + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +/** + * 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 environments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + factory()(root.lunr); + } +}(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 function(lunr) { + /* throw error if lunr is not yet included */ + if ('undefined' === typeof lunr) { + throw new Error('Lunr is not present. Please include / require Lunr before this script.'); + } + + /* throw error if lunr stemmer support is not yet included */ + if ('undefined' === typeof lunr.stemmerSupport) { + throw new Error('Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.'); + } + + /* + Japanese tokenization is trickier, since it does not + take into account spaces. + Since the tokenization function is represented different + internally for each of the Lunr versions, this had to be done + in order to try to try to pick the best way of doing this based + on the Lunr version + */ + var isLunr2 = lunr.version[0] == "2"; + + /* register specific locale function */ + lunr.jp = function() { + this.pipeline.reset(); + this.pipeline.add( + lunr.jp.stopWordFilter, + lunr.jp.stemmer + ); + + // change the tokenizer for japanese one + if (isLunr2) { // for lunr version 2.0.0 + this.tokenizer = lunr.jp.tokenizer; + } else { + if (lunr.tokenizer) { // for lunr version 0.6.0 + lunr.tokenizer = lunr.jp.tokenizer; + } + if (this.tokenizerFn) { // for lunr version 0.7.0 -> 1.0.0 + this.tokenizerFn = lunr.jp.tokenizer; + } + } + }; + var segmenter = new lunr.TinySegmenter(); // インスタンス生成 + + lunr.jp.tokenizer = function (obj) { + if (!arguments.length || obj == null || obj == undefined) return [] + if (Array.isArray(obj)) return obj.map(function (t) { return isLunr2 ? new lunr.Token(t.toLowerCase()) : t.toLowerCase() }) + + var str = obj.toString().toLowerCase().replace(/^\s+/, '') + + for (var i = str.length - 1; i >= 0; i--) { + if (/\S/.test(str.charAt(i))) { + str = str.substring(0, i + 1) + break + } + } + + var segs = segmenter.segment(str); // 単語の配列が返る + return segs.filter(function (token) { + return !!token + }) + .map(function (token) { + return isLunr2 ? new lunr.Token(token) : token + }) + } + + /* lunr stemmer function */ + lunr.jp.stemmer = (function() { + + /* TODO japanese stemmer */ + return function(word) { + return word; + } + })(); + + lunr.Pipeline.registerFunction(lunr.jp.stemmer, 'stemmer-jp'); + lunr.jp.wordCharacters = "一二三四五六七八九十百千万億兆一-龠々〆ヵヶぁ-んァ-ヴーア-ン゙a-zA-Za-zA-Z0-90-9"; + + /* stop word filter function */ + lunr.jp.stopWordFilter = function(token) { + if (lunr.jp.stopWordFilter.stopWords.indexOf(isLunr2 ? token.toString() : token) === -1) { + return token; + } + }; + + // stopword for japanese is from http://www.ranks.nl/stopwords/japanese + lunr.jp.stopWordFilter = lunr.generateStopWordFilter( + 'これ それ あれ この その あの ここ そこ あそこ こちら どこ だれ なに なん 何 私 貴方 貴方方 我々 私達 あの人 あのかた 彼女 彼 です あります おります います は が の に を で え から まで より も どの と し それで しかし'.split(' ')); + + lunr.Pipeline.registerFunction(lunr.jp.stopWordFilter, 'stopWordFilter-jp'); + }; +})) \ No newline at end of file diff --git a/mkdocs/contrib/search/lunr-language/lunr.multi.js b/mkdocs/contrib/search/lunr-language/lunr.multi.js new file mode 100644 index 0000000000..076792c724 --- /dev/null +++ b/mkdocs/contrib/search/lunr-language/lunr.multi.js @@ -0,0 +1,75 @@ +/** + * 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 environments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + factory()(root.lunr); + } +}(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 function(lunr) { + /* Set up the pipeline for indexing content in multiple languages. The + corresponding lunr.{lang} files must be loaded before calling this + function; English ('en') is built in. + + Returns: a lunr plugin for use in your indexer. + + Known drawback: every word will be stemmed with stemmers for every + language. This could mean that sometimes words that have the same + stemming root will not be stemmed as such. + */ + lunr.multiLanguage = function(/* lang1, lang2, ... */) { + var languages = Array.prototype.slice.call(arguments); + var nameSuffix = languages.join('-'); + var wordCharacters = ""; + var pipeline = []; + var searchPipeline = []; + for (var i = 0; i < languages.length; ++i) { + if (languages[i] == 'en') { + wordCharacters += '\\w'; + pipeline.unshift(lunr.stopWordFilter); + pipeline.push(lunr.stemmer); + searchPipeline.push(lunr.stemmer); + } else { + wordCharacters += lunr[languages[i]].wordCharacters; + pipeline.unshift(lunr[languages[i]].stopWordFilter); + pipeline.push(lunr[languages[i]].stemmer); + searchPipeline.push(lunr[languages[i]].stemmer); + } + }; + var multiTrimmer = lunr.trimmerSupport.generateTrimmer(wordCharacters); + lunr.Pipeline.registerFunction(multiTrimmer, 'lunr-multi-trimmer-' + nameSuffix); + pipeline.unshift(multiTrimmer); + + return function() { + this.pipeline.reset(); + + this.pipeline.add.apply(this.pipeline, pipeline); + + // for lunr version 2 + // this is necessary so that every searched word is also stemmed before + // in lunr <= 1 this is not needed, as it is done using the normal pipeline + if (this.searchPipeline) { + this.searchPipeline.reset(); + this.searchPipeline.add.apply(this.searchPipeline, searchPipeline); + } + }; + } + } +})); diff --git a/mkdocs/contrib/search/lunr-language/lunr.no.js b/mkdocs/contrib/search/lunr-language/lunr.no.js new file mode 100644 index 0000000000..34f8ab1aae --- /dev/null +++ b/mkdocs/contrib/search/lunr-language/lunr.no.js @@ -0,0 +1,258 @@ +/*! + * Lunr languages, `Norwegian` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +/** + * 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 environments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + factory()(root.lunr); + } +}(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 function(lunr) { + /* throw error if lunr is not yet included */ + if ('undefined' === typeof lunr) { + throw new Error('Lunr is not present. Please include / require Lunr before this script.'); + } + + /* throw error if lunr stemmer support is not yet included */ + if ('undefined' === typeof lunr.stemmerSupport) { + throw new Error('Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.'); + } + + /* register specific locale function */ + lunr.no = function() { + this.pipeline.reset(); + this.pipeline.add( + lunr.no.trimmer, + lunr.no.stopWordFilter, + lunr.no.stemmer + ); + + // for lunr version 2 + // this is necessary so that every searched word is also stemmed before + // in lunr <= 1 this is not needed, as it is done using the normal pipeline + if (this.searchPipeline) { + this.searchPipeline.reset(); + this.searchPipeline.add(lunr.no.stemmer) + } + }; + + /* lunr trimmer function */ + lunr.no.wordCharacters = "A-Za-z\xAA\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02B8\u02E0-\u02E4\u1D00-\u1D25\u1D2C-\u1D5C\u1D62-\u1D65\u1D6B-\u1D77\u1D79-\u1DBE\u1E00-\u1EFF\u2071\u207F\u2090-\u209C\u212A\u212B\u2132\u214E\u2160-\u2188\u2C60-\u2C7F\uA722-\uA787\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA7FF\uAB30-\uAB5A\uAB5C-\uAB64\uFB00-\uFB06\uFF21-\uFF3A\uFF41-\uFF5A"; + lunr.no.trimmer = lunr.trimmerSupport.generateTrimmer(lunr.no.wordCharacters); + + lunr.Pipeline.registerFunction(lunr.no.trimmer, 'trimmer-no'); + + /* lunr stemmer function */ + lunr.no.stemmer = (function() { + /* create the wrapped stemmer object */ + var Among = lunr.stemmerSupport.Among, + SnowballProgram = lunr.stemmerSupport.SnowballProgram, + st = new function NorwegianStemmer() { + var a_0 = [new Among("a", -1, 1), new Among("e", -1, 1), + new Among("ede", 1, 1), new Among("ande", 1, 1), + new Among("ende", 1, 1), new Among("ane", 1, 1), + new Among("ene", 1, 1), new Among("hetene", 6, 1), + new Among("erte", 1, 3), new Among("en", -1, 1), + new Among("heten", 9, 1), new Among("ar", -1, 1), + new Among("er", -1, 1), new Among("heter", 12, 1), + new Among("s", -1, 2), new Among("as", 14, 1), + new Among("es", 14, 1), new Among("edes", 16, 1), + new Among("endes", 16, 1), new Among("enes", 16, 1), + new Among("hetenes", 19, 1), new Among("ens", 14, 1), + new Among("hetens", 21, 1), new Among("ers", 14, 1), + new Among("ets", 14, 1), new Among("et", -1, 1), + new Among("het", 25, 1), new Among("ert", -1, 3), + new Among("ast", -1, 1) + ], + a_1 = [new Among("dt", -1, -1), + new Among("vt", -1, -1) + ], + a_2 = [new Among("leg", -1, 1), + new Among("eleg", 0, 1), new Among("ig", -1, 1), + new Among("eig", 2, 1), new Among("lig", 2, 1), + new Among("elig", 4, 1), new Among("els", -1, 1), + new Among("lov", -1, 1), new Among("elov", 7, 1), + new Among("slov", 7, 1), new Among("hetslov", 9, 1) + ], + g_v = [17, + 65, 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 128 + ], + g_s_ending = [ + 119, 125, 149, 1 + ], + I_x, I_p1, sbp = new SnowballProgram(); + this.setCurrent = function(word) { + sbp.setCurrent(word); + }; + this.getCurrent = function() { + return sbp.getCurrent(); + }; + + function r_mark_regions() { + var v_1, c = sbp.cursor + 3; + I_p1 = sbp.limit; + if (0 <= c || c <= sbp.limit) { + I_x = c; + while (true) { + v_1 = sbp.cursor; + if (sbp.in_grouping(g_v, 97, 248)) { + sbp.cursor = v_1; + break; + } + if (v_1 >= sbp.limit) + return; + sbp.cursor = v_1 + 1; + } + while (!sbp.out_grouping(g_v, 97, 248)) { + if (sbp.cursor >= sbp.limit) + return; + sbp.cursor++; + } + I_p1 = sbp.cursor; + if (I_p1 < I_x) + I_p1 = I_x; + } + } + + function r_main_suffix() { + var among_var, v_1, v_2; + if (sbp.cursor >= I_p1) { + v_1 = sbp.limit_backward; + sbp.limit_backward = I_p1; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_0, 29); + sbp.limit_backward = v_1; + if (among_var) { + sbp.bra = sbp.cursor; + switch (among_var) { + case 1: + sbp.slice_del(); + break; + case 2: + v_2 = sbp.limit - sbp.cursor; + if (sbp.in_grouping_b(g_s_ending, 98, 122)) + sbp.slice_del(); + else { + sbp.cursor = sbp.limit - v_2; + if (sbp.eq_s_b(1, "k") && + sbp.out_grouping_b(g_v, 97, 248)) + sbp.slice_del(); + } + break; + case 3: + sbp.slice_from("er"); + break; + } + } + } + } + + function r_consonant_pair() { + var v_1 = sbp.limit - sbp.cursor, + v_2; + if (sbp.cursor >= I_p1) { + v_2 = sbp.limit_backward; + sbp.limit_backward = I_p1; + sbp.ket = sbp.cursor; + if (sbp.find_among_b(a_1, 2)) { + sbp.bra = sbp.cursor; + sbp.limit_backward = v_2; + sbp.cursor = sbp.limit - v_1; + if (sbp.cursor > sbp.limit_backward) { + sbp.cursor--; + sbp.bra = sbp.cursor; + sbp.slice_del(); + } + } else + sbp.limit_backward = v_2; + } + } + + function r_other_suffix() { + var among_var, v_1; + if (sbp.cursor >= I_p1) { + v_1 = sbp.limit_backward; + sbp.limit_backward = I_p1; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_2, 11); + if (among_var) { + sbp.bra = sbp.cursor; + sbp.limit_backward = v_1; + if (among_var == 1) + sbp.slice_del(); + } else + sbp.limit_backward = v_1; + } + } + this.stem = function() { + var v_1 = sbp.cursor; + r_mark_regions(); + sbp.limit_backward = v_1; + sbp.cursor = sbp.limit; + r_main_suffix(); + sbp.cursor = sbp.limit; + r_consonant_pair(); + sbp.cursor = sbp.limit; + r_other_suffix(); + return true; + } + }; + + /* and return a function that stems a word for the current locale */ + return function(token) { + // for lunr version 2 + if (typeof token.update === "function") { + return token.update(function(word) { + st.setCurrent(word); + st.stem(); + return st.getCurrent(); + }) + } else { // for lunr version <= 1 + st.setCurrent(token); + st.stem(); + return st.getCurrent(); + } + } + })(); + + lunr.Pipeline.registerFunction(lunr.no.stemmer, 'stemmer-no'); + + lunr.no.stopWordFilter = lunr.generateStopWordFilter('alle at av bare begge ble blei bli blir blitt både båe da de deg dei deim deira deires dem den denne der dere deres det dette di din disse ditt du dykk dykkar då eg ein eit eitt eller elles en enn er et ett etter for fordi fra før ha hadde han hans har hennar henne hennes her hjå ho hoe honom hoss hossen hun hva hvem hver hvilke hvilken hvis hvor hvordan hvorfor i ikke ikkje ikkje ingen ingi inkje inn inni ja jeg kan kom korleis korso kun kunne kva kvar kvarhelst kven kvi kvifor man mange me med medan meg meget mellom men mi min mine mitt mot mykje ned no noe noen noka noko nokon nokor nokre nå når og også om opp oss over på samme seg selv si si sia sidan siden sin sine sitt sjøl skal skulle slik so som som somme somt så sånn til um upp ut uten var vart varte ved vere verte vi vil ville vore vors vort vår være være vært å'.split(' ')); + + lunr.Pipeline.registerFunction(lunr.no.stopWordFilter, 'stopWordFilter-no'); + }; +})) \ No newline at end of file diff --git a/mkdocs/contrib/search/lunr-language/lunr.pt.js b/mkdocs/contrib/search/lunr-language/lunr.pt.js new file mode 100644 index 0000000000..8834b907f1 --- /dev/null +++ b/mkdocs/contrib/search/lunr-language/lunr.pt.js @@ -0,0 +1,570 @@ +/*! + * Lunr languages, `Portuguese` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +/** + * 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 environments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + factory()(root.lunr); + } +}(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 function(lunr) { + /* throw error if lunr is not yet included */ + if ('undefined' === typeof lunr) { + throw new Error('Lunr is not present. Please include / require Lunr before this script.'); + } + + /* throw error if lunr stemmer support is not yet included */ + if ('undefined' === typeof lunr.stemmerSupport) { + throw new Error('Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.'); + } + + /* register specific locale function */ + lunr.pt = function() { + this.pipeline.reset(); + this.pipeline.add( + lunr.pt.trimmer, + lunr.pt.stopWordFilter, + lunr.pt.stemmer + ); + + // for lunr version 2 + // this is necessary so that every searched word is also stemmed before + // in lunr <= 1 this is not needed, as it is done using the normal pipeline + if (this.searchPipeline) { + this.searchPipeline.reset(); + this.searchPipeline.add(lunr.pt.stemmer) + } + }; + + /* lunr trimmer function */ + lunr.pt.wordCharacters = "A-Za-z\xAA\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02B8\u02E0-\u02E4\u1D00-\u1D25\u1D2C-\u1D5C\u1D62-\u1D65\u1D6B-\u1D77\u1D79-\u1DBE\u1E00-\u1EFF\u2071\u207F\u2090-\u209C\u212A\u212B\u2132\u214E\u2160-\u2188\u2C60-\u2C7F\uA722-\uA787\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA7FF\uAB30-\uAB5A\uAB5C-\uAB64\uFB00-\uFB06\uFF21-\uFF3A\uFF41-\uFF5A"; + lunr.pt.trimmer = lunr.trimmerSupport.generateTrimmer(lunr.pt.wordCharacters); + + lunr.Pipeline.registerFunction(lunr.pt.trimmer, 'trimmer-pt'); + + /* lunr stemmer function */ + lunr.pt.stemmer = (function() { + /* create the wrapped stemmer object */ + var Among = lunr.stemmerSupport.Among, + SnowballProgram = lunr.stemmerSupport.SnowballProgram, + st = new function PortugueseStemmer() { + var a_0 = [new Among("", -1, 3), new Among("\u00E3", 0, 1), + new Among("\u00F5", 0, 2) + ], + a_1 = [new Among("", -1, 3), + new Among("a~", 0, 1), new Among("o~", 0, 2) + ], + a_2 = [ + new Among("ic", -1, -1), new Among("ad", -1, -1), + new Among("os", -1, -1), new Among("iv", -1, 1) + ], + a_3 = [ + new Among("ante", -1, 1), new Among("avel", -1, 1), + new Among("\u00EDvel", -1, 1) + ], + a_4 = [new Among("ic", -1, 1), + new Among("abil", -1, 1), new Among("iv", -1, 1) + ], + a_5 = [ + new Among("ica", -1, 1), new Among("\u00E2ncia", -1, 1), + new Among("\u00EAncia", -1, 4), new Among("ira", -1, 9), + new Among("adora", -1, 1), new Among("osa", -1, 1), + new Among("ista", -1, 1), new Among("iva", -1, 8), + new Among("eza", -1, 1), new Among("log\u00EDa", -1, 2), + new Among("idade", -1, 7), new Among("ante", -1, 1), + new Among("mente", -1, 6), new Among("amente", 12, 5), + new Among("\u00E1vel", -1, 1), new Among("\u00EDvel", -1, 1), + new Among("uci\u00F3n", -1, 3), new Among("ico", -1, 1), + new Among("ismo", -1, 1), new Among("oso", -1, 1), + new Among("amento", -1, 1), new Among("imento", -1, 1), + new Among("ivo", -1, 8), new Among("a\u00E7a~o", -1, 1), + new Among("ador", -1, 1), new Among("icas", -1, 1), + new Among("\u00EAncias", -1, 4), new Among("iras", -1, 9), + new Among("adoras", -1, 1), new Among("osas", -1, 1), + new Among("istas", -1, 1), new Among("ivas", -1, 8), + new Among("ezas", -1, 1), new Among("log\u00EDas", -1, 2), + new Among("idades", -1, 7), new Among("uciones", -1, 3), + new Among("adores", -1, 1), new Among("antes", -1, 1), + new Among("a\u00E7o~es", -1, 1), new Among("icos", -1, 1), + new Among("ismos", -1, 1), new Among("osos", -1, 1), + new Among("amentos", -1, 1), new Among("imentos", -1, 1), + new Among("ivos", -1, 8) + ], + a_6 = [new Among("ada", -1, 1), + new Among("ida", -1, 1), new Among("ia", -1, 1), + new Among("aria", 2, 1), new Among("eria", 2, 1), + new Among("iria", 2, 1), new Among("ara", -1, 1), + new Among("era", -1, 1), new Among("ira", -1, 1), + new Among("ava", -1, 1), new Among("asse", -1, 1), + new Among("esse", -1, 1), new Among("isse", -1, 1), + new Among("aste", -1, 1), new Among("este", -1, 1), + new Among("iste", -1, 1), new Among("ei", -1, 1), + new Among("arei", 16, 1), new Among("erei", 16, 1), + new Among("irei", 16, 1), new Among("am", -1, 1), + new Among("iam", 20, 1), new Among("ariam", 21, 1), + new Among("eriam", 21, 1), new Among("iriam", 21, 1), + new Among("aram", 20, 1), new Among("eram", 20, 1), + new Among("iram", 20, 1), new Among("avam", 20, 1), + new Among("em", -1, 1), new Among("arem", 29, 1), + new Among("erem", 29, 1), new Among("irem", 29, 1), + new Among("assem", 29, 1), new Among("essem", 29, 1), + new Among("issem", 29, 1), new Among("ado", -1, 1), + new Among("ido", -1, 1), new Among("ando", -1, 1), + new Among("endo", -1, 1), new Among("indo", -1, 1), + new Among("ara~o", -1, 1), new Among("era~o", -1, 1), + new Among("ira~o", -1, 1), new Among("ar", -1, 1), + new Among("er", -1, 1), new Among("ir", -1, 1), + new Among("as", -1, 1), new Among("adas", 47, 1), + new Among("idas", 47, 1), new Among("ias", 47, 1), + new Among("arias", 50, 1), new Among("erias", 50, 1), + new Among("irias", 50, 1), new Among("aras", 47, 1), + new Among("eras", 47, 1), new Among("iras", 47, 1), + new Among("avas", 47, 1), new Among("es", -1, 1), + new Among("ardes", 58, 1), new Among("erdes", 58, 1), + new Among("irdes", 58, 1), new Among("ares", 58, 1), + new Among("eres", 58, 1), new Among("ires", 58, 1), + new Among("asses", 58, 1), new Among("esses", 58, 1), + new Among("isses", 58, 1), new Among("astes", 58, 1), + new Among("estes", 58, 1), new Among("istes", 58, 1), + new Among("is", -1, 1), new Among("ais", 71, 1), + new Among("eis", 71, 1), new Among("areis", 73, 1), + new Among("ereis", 73, 1), new Among("ireis", 73, 1), + new Among("\u00E1reis", 73, 1), new Among("\u00E9reis", 73, 1), + new Among("\u00EDreis", 73, 1), new Among("\u00E1sseis", 73, 1), + new Among("\u00E9sseis", 73, 1), new Among("\u00EDsseis", 73, 1), + new Among("\u00E1veis", 73, 1), new Among("\u00EDeis", 73, 1), + new Among("ar\u00EDeis", 84, 1), new Among("er\u00EDeis", 84, 1), + new Among("ir\u00EDeis", 84, 1), new Among("ados", -1, 1), + new Among("idos", -1, 1), new Among("amos", -1, 1), + new Among("\u00E1ramos", 90, 1), new Among("\u00E9ramos", 90, 1), + new Among("\u00EDramos", 90, 1), new Among("\u00E1vamos", 90, 1), + new Among("\u00EDamos", 90, 1), new Among("ar\u00EDamos", 95, 1), + new Among("er\u00EDamos", 95, 1), new Among("ir\u00EDamos", 95, 1), + new Among("emos", -1, 1), new Among("aremos", 99, 1), + new Among("eremos", 99, 1), new Among("iremos", 99, 1), + new Among("\u00E1ssemos", 99, 1), new Among("\u00EAssemos", 99, 1), + new Among("\u00EDssemos", 99, 1), new Among("imos", -1, 1), + new Among("armos", -1, 1), new Among("ermos", -1, 1), + new Among("irmos", -1, 1), new Among("\u00E1mos", -1, 1), + new Among("ar\u00E1s", -1, 1), new Among("er\u00E1s", -1, 1), + new Among("ir\u00E1s", -1, 1), new Among("eu", -1, 1), + new Among("iu", -1, 1), new Among("ou", -1, 1), + new Among("ar\u00E1", -1, 1), new Among("er\u00E1", -1, 1), + new Among("ir\u00E1", -1, 1) + ], + a_7 = [new Among("a", -1, 1), + new Among("i", -1, 1), new Among("o", -1, 1), + new Among("os", -1, 1), new Among("\u00E1", -1, 1), + new Among("\u00ED", -1, 1), new Among("\u00F3", -1, 1) + ], + a_8 = [ + new Among("e", -1, 1), new Among("\u00E7", -1, 2), + new Among("\u00E9", -1, 1), new Among("\u00EA", -1, 1) + ], + g_v = [17, + 65, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 19, 12, 2 + ], + I_p2, I_p1, I_pV, sbp = new SnowballProgram(); + this.setCurrent = function(word) { + sbp.setCurrent(word); + }; + this.getCurrent = function() { + return sbp.getCurrent(); + }; + + function r_prelude() { + var among_var; + while (true) { + sbp.bra = sbp.cursor; + among_var = sbp.find_among(a_0, 3); + if (among_var) { + sbp.ket = sbp.cursor; + switch (among_var) { + case 1: + sbp.slice_from("a~"); + continue; + case 2: + sbp.slice_from("o~"); + continue; + case 3: + if (sbp.cursor >= sbp.limit) + break; + sbp.cursor++; + continue; + } + } + break; + } + } + + function habr2() { + if (sbp.out_grouping(g_v, 97, 250)) { + while (!sbp.in_grouping(g_v, 97, 250)) { + if (sbp.cursor >= sbp.limit) + return true; + sbp.cursor++; + } + return false; + } + return true; + } + + function habr3() { + if (sbp.in_grouping(g_v, 97, 250)) { + while (!sbp.out_grouping(g_v, 97, 250)) { + if (sbp.cursor >= sbp.limit) + return false; + sbp.cursor++; + } + } + I_pV = sbp.cursor; + return true; + } + + function habr4() { + var v_1 = sbp.cursor, + v_2, v_3; + if (sbp.in_grouping(g_v, 97, 250)) { + v_2 = sbp.cursor; + if (habr2()) { + sbp.cursor = v_2; + if (habr3()) + return; + } else + I_pV = sbp.cursor; + } + sbp.cursor = v_1; + if (sbp.out_grouping(g_v, 97, 250)) { + v_3 = sbp.cursor; + if (habr2()) { + sbp.cursor = v_3; + if (!sbp.in_grouping(g_v, 97, 250) || sbp.cursor >= sbp.limit) + return; + sbp.cursor++; + } + I_pV = sbp.cursor; + } + } + + function habr5() { + while (!sbp.in_grouping(g_v, 97, 250)) { + if (sbp.cursor >= sbp.limit) + return false; + sbp.cursor++; + } + while (!sbp.out_grouping(g_v, 97, 250)) { + if (sbp.cursor >= sbp.limit) + return false; + sbp.cursor++; + } + return true; + } + + function r_mark_regions() { + var v_1 = sbp.cursor; + I_pV = sbp.limit; + I_p1 = I_pV; + I_p2 = I_pV; + habr4(); + sbp.cursor = v_1; + if (habr5()) { + I_p1 = sbp.cursor; + if (habr5()) + I_p2 = sbp.cursor; + } + } + + function r_postlude() { + var among_var; + while (true) { + sbp.bra = sbp.cursor; + among_var = sbp.find_among(a_1, 3); + if (among_var) { + sbp.ket = sbp.cursor; + switch (among_var) { + case 1: + sbp.slice_from("\u00E3"); + continue; + case 2: + sbp.slice_from("\u00F5"); + continue; + case 3: + if (sbp.cursor >= sbp.limit) + break; + sbp.cursor++; + continue; + } + } + break; + } + } + + function r_RV() { + return I_pV <= sbp.cursor; + } + + function r_R1() { + return I_p1 <= sbp.cursor; + } + + function r_R2() { + return I_p2 <= sbp.cursor; + } + + function r_standard_suffix() { + var among_var; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_5, 45); + if (!among_var) + return false; + sbp.bra = sbp.cursor; + switch (among_var) { + case 1: + if (!r_R2()) + return false; + sbp.slice_del(); + break; + case 2: + if (!r_R2()) + return false; + sbp.slice_from("log"); + break; + case 3: + if (!r_R2()) + return false; + sbp.slice_from("u"); + break; + case 4: + if (!r_R2()) + return false; + sbp.slice_from("ente"); + break; + case 5: + if (!r_R1()) + return false; + sbp.slice_del(); + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_2, 4); + if (among_var) { + sbp.bra = sbp.cursor; + if (r_R2()) { + sbp.slice_del(); + if (among_var == 1) { + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(2, "at")) { + sbp.bra = sbp.cursor; + if (r_R2()) + sbp.slice_del(); + } + } + } + } + break; + case 6: + if (!r_R2()) + return false; + sbp.slice_del(); + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_3, 3); + if (among_var) { + sbp.bra = sbp.cursor; + if (among_var == 1) + if (r_R2()) + sbp.slice_del(); + } + break; + case 7: + if (!r_R2()) + return false; + sbp.slice_del(); + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_4, 3); + if (among_var) { + sbp.bra = sbp.cursor; + if (among_var == 1) + if (r_R2()) + sbp.slice_del(); + } + break; + case 8: + if (!r_R2()) + return false; + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(2, "at")) { + sbp.bra = sbp.cursor; + if (r_R2()) + sbp.slice_del(); + } + break; + case 9: + if (!r_RV() || !sbp.eq_s_b(1, "e")) + return false; + sbp.slice_from("ir"); + break; + } + return true; + } + + function r_verb_suffix() { + var among_var, v_1; + if (sbp.cursor >= I_pV) { + v_1 = sbp.limit_backward; + sbp.limit_backward = I_pV; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_6, 120); + if (among_var) { + sbp.bra = sbp.cursor; + if (among_var == 1) + sbp.slice_del(); + sbp.limit_backward = v_1; + return true; + } + sbp.limit_backward = v_1; + } + return false; + } + + function r_residual_suffix() { + var among_var; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_7, 7); + if (among_var) { + sbp.bra = sbp.cursor; + if (among_var == 1) + if (r_RV()) + sbp.slice_del(); + } + } + + function habr6(c1, c2) { + if (sbp.eq_s_b(1, c1)) { + sbp.bra = sbp.cursor; + var v_1 = sbp.limit - sbp.cursor; + if (sbp.eq_s_b(1, c2)) { + sbp.cursor = sbp.limit - v_1; + if (r_RV()) + sbp.slice_del(); + return false; + } + } + return true; + } + + function r_residual_form() { + var among_var, v_1, v_2, v_3; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_8, 4); + if (among_var) { + sbp.bra = sbp.cursor; + switch (among_var) { + case 1: + if (r_RV()) { + sbp.slice_del(); + sbp.ket = sbp.cursor; + v_1 = sbp.limit - sbp.cursor; + if (habr6("u", "g")) + habr6("i", "c") + } + break; + case 2: + sbp.slice_from("c"); + break; + } + } + } + + function habr1() { + if (!r_standard_suffix()) { + sbp.cursor = sbp.limit; + if (!r_verb_suffix()) { + sbp.cursor = sbp.limit; + r_residual_suffix(); + return; + } + } + sbp.cursor = sbp.limit; + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(1, "i")) { + sbp.bra = sbp.cursor; + if (sbp.eq_s_b(1, "c")) { + sbp.cursor = sbp.limit; + if (r_RV()) + sbp.slice_del(); + } + } + } + this.stem = function() { + var v_1 = sbp.cursor; + r_prelude(); + sbp.cursor = v_1; + r_mark_regions(); + sbp.limit_backward = v_1; + sbp.cursor = sbp.limit; + habr1(); + sbp.cursor = sbp.limit; + r_residual_form(); + sbp.cursor = sbp.limit_backward; + r_postlude(); + return true; + } + }; + + /* and return a function that stems a word for the current locale */ + return function(token) { + // for lunr version 2 + if (typeof token.update === "function") { + return token.update(function(word) { + st.setCurrent(word); + st.stem(); + return st.getCurrent(); + }) + } else { // for lunr version <= 1 + st.setCurrent(token); + st.stem(); + return st.getCurrent(); + } + } + })(); + + lunr.Pipeline.registerFunction(lunr.pt.stemmer, 'stemmer-pt'); + + lunr.pt.stopWordFilter = lunr.generateStopWordFilter('a ao aos aquela aquelas aquele aqueles aquilo as até com como da das de dela delas dele deles depois do dos e ela elas ele eles em entre era eram essa essas esse esses esta estamos estas estava estavam este esteja estejam estejamos estes esteve estive estivemos estiver estivera estiveram estiverem estivermos estivesse estivessem estivéramos estivéssemos estou está estávamos estão eu foi fomos for fora foram forem formos fosse fossem fui fôramos fôssemos haja hajam hajamos havemos hei houve houvemos houver houvera houveram houverei houverem houveremos houveria houveriam houvermos houverá houverão houveríamos houvesse houvessem houvéramos houvéssemos há hão isso isto já lhe lhes mais mas me mesmo meu meus minha minhas muito na nas nem no nos nossa nossas nosso nossos num numa não nós o os ou para pela pelas pelo pelos por qual quando que quem se seja sejam sejamos sem serei seremos seria seriam será serão seríamos seu seus somos sou sua suas são só também te tem temos tenha tenham tenhamos tenho terei teremos teria teriam terá terão teríamos teu teus teve tinha tinham tive tivemos tiver tivera tiveram tiverem tivermos tivesse tivessem tivéramos tivéssemos tu tua tuas tém tínhamos um uma você vocês vos à às éramos'.split(' ')); + + lunr.Pipeline.registerFunction(lunr.pt.stopWordFilter, 'stopWordFilter-pt'); + }; +})) \ No newline at end of file diff --git a/mkdocs/contrib/search/lunr-language/lunr.ro.js b/mkdocs/contrib/search/lunr-language/lunr.ro.js new file mode 100644 index 0000000000..9659b76858 --- /dev/null +++ b/mkdocs/contrib/search/lunr-language/lunr.ro.js @@ -0,0 +1,558 @@ +/*! + * Lunr languages, `Romanian` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +/** + * 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 environments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + factory()(root.lunr); + } +}(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 function(lunr) { + /* throw error if lunr is not yet included */ + if ('undefined' === typeof lunr) { + throw new Error('Lunr is not present. Please include / require Lunr before this script.'); + } + + /* throw error if lunr stemmer support is not yet included */ + if ('undefined' === typeof lunr.stemmerSupport) { + throw new Error('Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.'); + } + + /* register specific locale function */ + lunr.ro = function() { + this.pipeline.reset(); + this.pipeline.add( + lunr.ro.trimmer, + lunr.ro.stopWordFilter, + lunr.ro.stemmer + ); + + // for lunr version 2 + // this is necessary so that every searched word is also stemmed before + // in lunr <= 1 this is not needed, as it is done using the normal pipeline + if (this.searchPipeline) { + this.searchPipeline.reset(); + this.searchPipeline.add(lunr.ro.stemmer) + } + }; + + /* lunr trimmer function */ + lunr.ro.wordCharacters = "A-Za-z\xAA\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02B8\u02E0-\u02E4\u1D00-\u1D25\u1D2C-\u1D5C\u1D62-\u1D65\u1D6B-\u1D77\u1D79-\u1DBE\u1E00-\u1EFF\u2071\u207F\u2090-\u209C\u212A\u212B\u2132\u214E\u2160-\u2188\u2C60-\u2C7F\uA722-\uA787\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA7FF\uAB30-\uAB5A\uAB5C-\uAB64\uFB00-\uFB06\uFF21-\uFF3A\uFF41-\uFF5A"; + lunr.ro.trimmer = lunr.trimmerSupport.generateTrimmer(lunr.ro.wordCharacters); + + lunr.Pipeline.registerFunction(lunr.ro.trimmer, 'trimmer-ro'); + + /* lunr stemmer function */ + lunr.ro.stemmer = (function() { + /* create the wrapped stemmer object */ + var Among = lunr.stemmerSupport.Among, + SnowballProgram = lunr.stemmerSupport.SnowballProgram, + st = new function RomanianStemmer() { + var a_0 = [new Among("", -1, 3), new Among("I", 0, 1), new Among("U", 0, 2)], + a_1 = [ + new Among("ea", -1, 3), new Among("a\u0163ia", -1, 7), + new Among("aua", -1, 2), new Among("iua", -1, 4), + new Among("a\u0163ie", -1, 7), new Among("ele", -1, 3), + new Among("ile", -1, 5), new Among("iile", 6, 4), + new Among("iei", -1, 4), new Among("atei", -1, 6), + new Among("ii", -1, 4), new Among("ului", -1, 1), + new Among("ul", -1, 1), new Among("elor", -1, 3), + new Among("ilor", -1, 4), new Among("iilor", 14, 4) + ], + a_2 = [ + new Among("icala", -1, 4), new Among("iciva", -1, 4), + new Among("ativa", -1, 5), new Among("itiva", -1, 6), + new Among("icale", -1, 4), new Among("a\u0163iune", -1, 5), + new Among("i\u0163iune", -1, 6), new Among("atoare", -1, 5), + new Among("itoare", -1, 6), new Among("\u0103toare", -1, 5), + new Among("icitate", -1, 4), new Among("abilitate", -1, 1), + new Among("ibilitate", -1, 2), new Among("ivitate", -1, 3), + new Among("icive", -1, 4), new Among("ative", -1, 5), + new Among("itive", -1, 6), new Among("icali", -1, 4), + new Among("atori", -1, 5), new Among("icatori", 18, 4), + new Among("itori", -1, 6), new Among("\u0103tori", -1, 5), + new Among("icitati", -1, 4), new Among("abilitati", -1, 1), + new Among("ivitati", -1, 3), new Among("icivi", -1, 4), + new Among("ativi", -1, 5), new Among("itivi", -1, 6), + new Among("icit\u0103i", -1, 4), new Among("abilit\u0103i", -1, 1), + new Among("ivit\u0103i", -1, 3), + new Among("icit\u0103\u0163i", -1, 4), + new Among("abilit\u0103\u0163i", -1, 1), + new Among("ivit\u0103\u0163i", -1, 3), new Among("ical", -1, 4), + new Among("ator", -1, 5), new Among("icator", 35, 4), + new Among("itor", -1, 6), new Among("\u0103tor", -1, 5), + new Among("iciv", -1, 4), new Among("ativ", -1, 5), + new Among("itiv", -1, 6), new Among("ical\u0103", -1, 4), + new Among("iciv\u0103", -1, 4), new Among("ativ\u0103", -1, 5), + new Among("itiv\u0103", -1, 6) + ], + a_3 = [new Among("ica", -1, 1), + new Among("abila", -1, 1), new Among("ibila", -1, 1), + new Among("oasa", -1, 1), new Among("ata", -1, 1), + new Among("ita", -1, 1), new Among("anta", -1, 1), + new Among("ista", -1, 3), new Among("uta", -1, 1), + new Among("iva", -1, 1), new Among("ic", -1, 1), + new Among("ice", -1, 1), new Among("abile", -1, 1), + new Among("ibile", -1, 1), new Among("isme", -1, 3), + new Among("iune", -1, 2), new Among("oase", -1, 1), + new Among("ate", -1, 1), new Among("itate", 17, 1), + new Among("ite", -1, 1), new Among("ante", -1, 1), + new Among("iste", -1, 3), new Among("ute", -1, 1), + new Among("ive", -1, 1), new Among("ici", -1, 1), + new Among("abili", -1, 1), new Among("ibili", -1, 1), + new Among("iuni", -1, 2), new Among("atori", -1, 1), + new Among("osi", -1, 1), new Among("ati", -1, 1), + new Among("itati", 30, 1), new Among("iti", -1, 1), + new Among("anti", -1, 1), new Among("isti", -1, 3), + new Among("uti", -1, 1), new Among("i\u015Fti", -1, 3), + new Among("ivi", -1, 1), new Among("it\u0103i", -1, 1), + new Among("o\u015Fi", -1, 1), new Among("it\u0103\u0163i", -1, 1), + new Among("abil", -1, 1), new Among("ibil", -1, 1), + new Among("ism", -1, 3), new Among("ator", -1, 1), + new Among("os", -1, 1), new Among("at", -1, 1), + new Among("it", -1, 1), new Among("ant", -1, 1), + new Among("ist", -1, 3), new Among("ut", -1, 1), + new Among("iv", -1, 1), new Among("ic\u0103", -1, 1), + new Among("abil\u0103", -1, 1), new Among("ibil\u0103", -1, 1), + new Among("oas\u0103", -1, 1), new Among("at\u0103", -1, 1), + new Among("it\u0103", -1, 1), new Among("ant\u0103", -1, 1), + new Among("ist\u0103", -1, 3), new Among("ut\u0103", -1, 1), + new Among("iv\u0103", -1, 1) + ], + a_4 = [new Among("ea", -1, 1), + new Among("ia", -1, 1), new Among("esc", -1, 1), + new Among("\u0103sc", -1, 1), new Among("ind", -1, 1), + new Among("\u00E2nd", -1, 1), new Among("are", -1, 1), + new Among("ere", -1, 1), new Among("ire", -1, 1), + new Among("\u00E2re", -1, 1), new Among("se", -1, 2), + new Among("ase", 10, 1), new Among("sese", 10, 2), + new Among("ise", 10, 1), new Among("use", 10, 1), + new Among("\u00E2se", 10, 1), new Among("e\u015Fte", -1, 1), + new Among("\u0103\u015Fte", -1, 1), new Among("eze", -1, 1), + new Among("ai", -1, 1), new Among("eai", 19, 1), + new Among("iai", 19, 1), new Among("sei", -1, 2), + new Among("e\u015Fti", -1, 1), new Among("\u0103\u015Fti", -1, 1), + new Among("ui", -1, 1), new Among("ezi", -1, 1), + new Among("\u00E2i", -1, 1), new Among("a\u015Fi", -1, 1), + new Among("se\u015Fi", -1, 2), new Among("ase\u015Fi", 29, 1), + new Among("sese\u015Fi", 29, 2), new Among("ise\u015Fi", 29, 1), + new Among("use\u015Fi", 29, 1), + new Among("\u00E2se\u015Fi", 29, 1), new Among("i\u015Fi", -1, 1), + new Among("u\u015Fi", -1, 1), new Among("\u00E2\u015Fi", -1, 1), + new Among("a\u0163i", -1, 2), new Among("ea\u0163i", 38, 1), + new Among("ia\u0163i", 38, 1), new Among("e\u0163i", -1, 2), + new Among("i\u0163i", -1, 2), new Among("\u00E2\u0163i", -1, 2), + new Among("ar\u0103\u0163i", -1, 1), + new Among("ser\u0103\u0163i", -1, 2), + new Among("aser\u0103\u0163i", 45, 1), + new Among("seser\u0103\u0163i", 45, 2), + new Among("iser\u0103\u0163i", 45, 1), + new Among("user\u0103\u0163i", 45, 1), + new Among("\u00E2ser\u0103\u0163i", 45, 1), + new Among("ir\u0103\u0163i", -1, 1), + new Among("ur\u0103\u0163i", -1, 1), + new Among("\u00E2r\u0103\u0163i", -1, 1), new Among("am", -1, 1), + new Among("eam", 54, 1), new Among("iam", 54, 1), + new Among("em", -1, 2), new Among("asem", 57, 1), + new Among("sesem", 57, 2), new Among("isem", 57, 1), + new Among("usem", 57, 1), new Among("\u00E2sem", 57, 1), + new Among("im", -1, 2), new Among("\u00E2m", -1, 2), + new Among("\u0103m", -1, 2), new Among("ar\u0103m", 65, 1), + new Among("ser\u0103m", 65, 2), new Among("aser\u0103m", 67, 1), + new Among("seser\u0103m", 67, 2), new Among("iser\u0103m", 67, 1), + new Among("user\u0103m", 67, 1), + new Among("\u00E2ser\u0103m", 67, 1), + new Among("ir\u0103m", 65, 1), new Among("ur\u0103m", 65, 1), + new Among("\u00E2r\u0103m", 65, 1), new Among("au", -1, 1), + new Among("eau", 76, 1), new Among("iau", 76, 1), + new Among("indu", -1, 1), new Among("\u00E2ndu", -1, 1), + new Among("ez", -1, 1), new Among("easc\u0103", -1, 1), + new Among("ar\u0103", -1, 1), new Among("ser\u0103", -1, 2), + new Among("aser\u0103", 84, 1), new Among("seser\u0103", 84, 2), + new Among("iser\u0103", 84, 1), new Among("user\u0103", 84, 1), + new Among("\u00E2ser\u0103", 84, 1), new Among("ir\u0103", -1, 1), + new Among("ur\u0103", -1, 1), new Among("\u00E2r\u0103", -1, 1), + new Among("eaz\u0103", -1, 1) + ], + a_5 = [new Among("a", -1, 1), + new Among("e", -1, 1), new Among("ie", 1, 1), + new Among("i", -1, 1), new Among("\u0103", -1, 1) + ], + g_v = [17, 65, + 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 32, 0, 0, 4 + ], + B_standard_suffix_removed, I_p2, I_p1, I_pV, sbp = new SnowballProgram(); + this.setCurrent = function(word) { + sbp.setCurrent(word); + }; + this.getCurrent = function() { + return sbp.getCurrent(); + }; + + function habr1(c1, c2) { + if (sbp.eq_s(1, c1)) { + sbp.ket = sbp.cursor; + if (sbp.in_grouping(g_v, 97, 259)) + sbp.slice_from(c2); + } + } + + function r_prelude() { + var v_1, v_2; + while (true) { + v_1 = sbp.cursor; + if (sbp.in_grouping(g_v, 97, 259)) { + v_2 = sbp.cursor; + sbp.bra = v_2; + habr1("u", "U"); + sbp.cursor = v_2; + habr1("i", "I"); + } + sbp.cursor = v_1; + if (sbp.cursor >= sbp.limit) { + break; + } + sbp.cursor++; + } + } + + function habr2() { + if (sbp.out_grouping(g_v, 97, 259)) { + while (!sbp.in_grouping(g_v, 97, 259)) { + if (sbp.cursor >= sbp.limit) + return true; + sbp.cursor++; + } + return false; + } + return true; + } + + function habr3() { + if (sbp.in_grouping(g_v, 97, 259)) { + while (!sbp.out_grouping(g_v, 97, 259)) { + if (sbp.cursor >= sbp.limit) + return true; + sbp.cursor++; + } + } + return false; + } + + function habr4() { + var v_1 = sbp.cursor, + v_2, v_3; + if (sbp.in_grouping(g_v, 97, 259)) { + v_2 = sbp.cursor; + if (habr2()) { + sbp.cursor = v_2; + if (!habr3()) { + I_pV = sbp.cursor; + return; + } + } else { + I_pV = sbp.cursor; + return; + } + } + sbp.cursor = v_1; + if (sbp.out_grouping(g_v, 97, 259)) { + v_3 = sbp.cursor; + if (habr2()) { + sbp.cursor = v_3; + if (sbp.in_grouping(g_v, 97, 259) && sbp.cursor < sbp.limit) + sbp.cursor++; + } + I_pV = sbp.cursor; + } + } + + function habr5() { + while (!sbp.in_grouping(g_v, 97, 259)) { + if (sbp.cursor >= sbp.limit) + return false; + sbp.cursor++; + } + while (!sbp.out_grouping(g_v, 97, 259)) { + if (sbp.cursor >= sbp.limit) + return false; + sbp.cursor++; + } + return true; + } + + function r_mark_regions() { + var v_1 = sbp.cursor; + I_pV = sbp.limit; + I_p1 = I_pV; + I_p2 = I_pV; + habr4(); + sbp.cursor = v_1; + if (habr5()) { + I_p1 = sbp.cursor; + if (habr5()) + I_p2 = sbp.cursor; + } + } + + function r_postlude() { + var among_var; + while (true) { + sbp.bra = sbp.cursor; + among_var = sbp.find_among(a_0, 3); + if (among_var) { + sbp.ket = sbp.cursor; + switch (among_var) { + case 1: + sbp.slice_from("i"); + continue; + case 2: + sbp.slice_from("u"); + continue; + case 3: + if (sbp.cursor >= sbp.limit) + break; + sbp.cursor++; + continue; + } + } + break; + } + } + + function r_RV() { + return I_pV <= sbp.cursor; + } + + function r_R1() { + return I_p1 <= sbp.cursor; + } + + function r_R2() { + return I_p2 <= sbp.cursor; + } + + function r_step_0() { + var among_var, v_1; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_1, 16); + if (among_var) { + sbp.bra = sbp.cursor; + if (r_R1()) { + switch (among_var) { + case 1: + sbp.slice_del(); + break; + case 2: + sbp.slice_from("a"); + break; + case 3: + sbp.slice_from("e"); + break; + case 4: + sbp.slice_from("i"); + break; + case 5: + v_1 = sbp.limit - sbp.cursor; + if (!sbp.eq_s_b(2, "ab")) { + sbp.cursor = sbp.limit - v_1; + sbp.slice_from("i"); + } + break; + case 6: + sbp.slice_from("at"); + break; + case 7: + sbp.slice_from("a\u0163i"); + break; + } + } + } + } + + function r_combo_suffix() { + var among_var, v_1 = sbp.limit - sbp.cursor; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_2, 46); + if (among_var) { + sbp.bra = sbp.cursor; + if (r_R1()) { + switch (among_var) { + case 1: + sbp.slice_from("abil"); + break; + case 2: + sbp.slice_from("ibil"); + break; + case 3: + sbp.slice_from("iv"); + break; + case 4: + sbp.slice_from("ic"); + break; + case 5: + sbp.slice_from("at"); + break; + case 6: + sbp.slice_from("it"); + break; + } + B_standard_suffix_removed = true; + sbp.cursor = sbp.limit - v_1; + return true; + } + } + return false; + } + + function r_standard_suffix() { + var among_var, v_1; + B_standard_suffix_removed = false; + while (true) { + v_1 = sbp.limit - sbp.cursor; + if (!r_combo_suffix()) { + sbp.cursor = sbp.limit - v_1; + break; + } + } + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_3, 62); + if (among_var) { + sbp.bra = sbp.cursor; + if (r_R2()) { + switch (among_var) { + case 1: + sbp.slice_del(); + break; + case 2: + if (sbp.eq_s_b(1, "\u0163")) { + sbp.bra = sbp.cursor; + sbp.slice_from("t"); + } + break; + case 3: + sbp.slice_from("ist"); + break; + } + B_standard_suffix_removed = true; + } + } + } + + function r_verb_suffix() { + var among_var, v_1, v_2; + if (sbp.cursor >= I_pV) { + v_1 = sbp.limit_backward; + sbp.limit_backward = I_pV; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_4, 94); + if (among_var) { + sbp.bra = sbp.cursor; + switch (among_var) { + case 1: + v_2 = sbp.limit - sbp.cursor; + if (!sbp.out_grouping_b(g_v, 97, 259)) { + sbp.cursor = sbp.limit - v_2; + if (!sbp.eq_s_b(1, "u")) + break; + } + case 2: + sbp.slice_del(); + break; + } + } + sbp.limit_backward = v_1; + } + } + + function r_vowel_suffix() { + var among_var; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_5, 5); + if (among_var) { + sbp.bra = sbp.cursor; + if (r_RV() && among_var == 1) + sbp.slice_del(); + } + } + this.stem = function() { + var v_1 = sbp.cursor; + r_prelude(); + sbp.cursor = v_1; + r_mark_regions(); + sbp.limit_backward = v_1; + sbp.cursor = sbp.limit; + r_step_0(); + sbp.cursor = sbp.limit; + r_standard_suffix(); + sbp.cursor = sbp.limit; + if (!B_standard_suffix_removed) { + sbp.cursor = sbp.limit; + r_verb_suffix(); + sbp.cursor = sbp.limit; + } + r_vowel_suffix(); + sbp.cursor = sbp.limit_backward; + r_postlude(); + return true; + } + }; + + /* and return a function that stems a word for the current locale */ + return function(token) { + // for lunr version 2 + if (typeof token.update === "function") { + return token.update(function(word) { + st.setCurrent(word); + st.stem(); + return st.getCurrent(); + }) + } else { // for lunr version <= 1 + st.setCurrent(token); + st.stem(); + return st.getCurrent(); + } + } + })(); + + lunr.Pipeline.registerFunction(lunr.ro.stemmer, 'stemmer-ro'); + + lunr.ro.stopWordFilter = lunr.generateStopWordFilter('acea aceasta această aceea acei aceia acel acela acele acelea acest acesta aceste acestea aceşti aceştia acolo acord acum ai aia aibă aici al ale alea altceva altcineva am ar are asemenea asta astea astăzi asupra au avea avem aveţi azi aş aşadar aţi bine bucur bună ca care caut ce cel ceva chiar cinci cine cineva contra cu cum cumva curând curînd când cât câte câtva câţi cînd cît cîte cîtva cîţi că căci cărei căror cărui către da dacă dar datorită dată dau de deci deja deoarece departe deşi din dinaintea dintr- dintre doi doilea două drept după dă ea ei el ele eram este eu eşti face fata fi fie fiecare fii fim fiu fiţi frumos fără graţie halbă iar ieri la le li lor lui lângă lîngă mai mea mei mele mereu meu mi mie mine mult multă mulţi mulţumesc mâine mîine mă ne nevoie nici nicăieri nimeni nimeri nimic nişte noastre noastră noi noroc nostru nouă noştri nu opt ori oricare orice oricine oricum oricând oricât oricînd oricît oriunde patra patru patrulea pe pentru peste pic poate pot prea prima primul prin puţin puţina puţină până pînă rog sa sale sau se spate spre sub sunt suntem sunteţi sută sînt sîntem sînteţi să săi său ta tale te timp tine toate toată tot totuşi toţi trei treia treilea tu tăi tău un una unde undeva unei uneia unele uneori unii unor unora unu unui unuia unul vi voastre voastră voi vostru vouă voştri vreme vreo vreun vă zece zero zi zice îi îl îmi împotriva în înainte înaintea încotro încât încît între întrucât întrucît îţi ăla ălea ăsta ăstea ăştia şapte şase şi ştiu ţi ţie'.split(' ')); + + lunr.Pipeline.registerFunction(lunr.ro.stopWordFilter, 'stopWordFilter-ro'); + }; +})) \ No newline at end of file diff --git a/mkdocs/contrib/search/lunr-language/lunr.ru.js b/mkdocs/contrib/search/lunr-language/lunr.ru.js new file mode 100644 index 0000000000..3e79452523 --- /dev/null +++ b/mkdocs/contrib/search/lunr-language/lunr.ru.js @@ -0,0 +1,391 @@ +/*! + * Lunr languages, `Russian` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +/** + * 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 environments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + factory()(root.lunr); + } +}(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 function(lunr) { + /* throw error if lunr is not yet included */ + if ('undefined' === typeof lunr) { + throw new Error('Lunr is not present. Please include / require Lunr before this script.'); + } + + /* throw error if lunr stemmer support is not yet included */ + if ('undefined' === typeof lunr.stemmerSupport) { + throw new Error('Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.'); + } + + /* register specific locale function */ + lunr.ru = function() { + this.pipeline.reset(); + this.pipeline.add( + lunr.ru.trimmer, + lunr.ru.stopWordFilter, + lunr.ru.stemmer + ); + + // for lunr version 2 + // this is necessary so that every searched word is also stemmed before + // in lunr <= 1 this is not needed, as it is done using the normal pipeline + if (this.searchPipeline) { + this.searchPipeline.reset(); + this.searchPipeline.add(lunr.ru.stemmer) + } + }; + + /* lunr trimmer function */ + lunr.ru.wordCharacters = "\u0400-\u0484\u0487-\u052F\u1D2B\u1D78\u2DE0-\u2DFF\uA640-\uA69F\uFE2E\uFE2F"; + lunr.ru.trimmer = lunr.trimmerSupport.generateTrimmer(lunr.ru.wordCharacters); + + lunr.Pipeline.registerFunction(lunr.ru.trimmer, 'trimmer-ru'); + + /* lunr stemmer function */ + lunr.ru.stemmer = (function() { + /* create the wrapped stemmer object */ + var Among = lunr.stemmerSupport.Among, + SnowballProgram = lunr.stemmerSupport.SnowballProgram, + st = new function RussianStemmer() { + var a_0 = [new Among("\u0432", -1, 1), new Among("\u0438\u0432", 0, 2), + new Among("\u044B\u0432", 0, 2), + new Among("\u0432\u0448\u0438", -1, 1), + new Among("\u0438\u0432\u0448\u0438", 3, 2), + new Among("\u044B\u0432\u0448\u0438", 3, 2), + new Among("\u0432\u0448\u0438\u0441\u044C", -1, 1), + new Among("\u0438\u0432\u0448\u0438\u0441\u044C", 6, 2), + new Among("\u044B\u0432\u0448\u0438\u0441\u044C", 6, 2) + ], + a_1 = [ + new Among("\u0435\u0435", -1, 1), new Among("\u0438\u0435", -1, 1), + new Among("\u043E\u0435", -1, 1), new Among("\u044B\u0435", -1, 1), + new Among("\u0438\u043C\u0438", -1, 1), + new Among("\u044B\u043C\u0438", -1, 1), + new Among("\u0435\u0439", -1, 1), new Among("\u0438\u0439", -1, 1), + new Among("\u043E\u0439", -1, 1), new Among("\u044B\u0439", -1, 1), + new Among("\u0435\u043C", -1, 1), new Among("\u0438\u043C", -1, 1), + new Among("\u043E\u043C", -1, 1), new Among("\u044B\u043C", -1, 1), + new Among("\u0435\u0433\u043E", -1, 1), + new Among("\u043E\u0433\u043E", -1, 1), + new Among("\u0435\u043C\u0443", -1, 1), + new Among("\u043E\u043C\u0443", -1, 1), + new Among("\u0438\u0445", -1, 1), new Among("\u044B\u0445", -1, 1), + new Among("\u0435\u044E", -1, 1), new Among("\u043E\u044E", -1, 1), + new Among("\u0443\u044E", -1, 1), new Among("\u044E\u044E", -1, 1), + new Among("\u0430\u044F", -1, 1), new Among("\u044F\u044F", -1, 1) + ], + a_2 = [ + new Among("\u0435\u043C", -1, 1), new Among("\u043D\u043D", -1, 1), + new Among("\u0432\u0448", -1, 1), + new Among("\u0438\u0432\u0448", 2, 2), + new Among("\u044B\u0432\u0448", 2, 2), new Among("\u0449", -1, 1), + new Among("\u044E\u0449", 5, 1), + new Among("\u0443\u044E\u0449", 6, 2) + ], + a_3 = [ + new Among("\u0441\u044C", -1, 1), new Among("\u0441\u044F", -1, 1) + ], + a_4 = [ + new Among("\u043B\u0430", -1, 1), + new Among("\u0438\u043B\u0430", 0, 2), + new Among("\u044B\u043B\u0430", 0, 2), + new Among("\u043D\u0430", -1, 1), + new Among("\u0435\u043D\u0430", 3, 2), + new Among("\u0435\u0442\u0435", -1, 1), + new Among("\u0438\u0442\u0435", -1, 2), + new Among("\u0439\u0442\u0435", -1, 1), + new Among("\u0435\u0439\u0442\u0435", 7, 2), + new Among("\u0443\u0439\u0442\u0435", 7, 2), + new Among("\u043B\u0438", -1, 1), + new Among("\u0438\u043B\u0438", 10, 2), + new Among("\u044B\u043B\u0438", 10, 2), new Among("\u0439", -1, 1), + new Among("\u0435\u0439", 13, 2), new Among("\u0443\u0439", 13, 2), + new Among("\u043B", -1, 1), new Among("\u0438\u043B", 16, 2), + new Among("\u044B\u043B", 16, 2), new Among("\u0435\u043C", -1, 1), + new Among("\u0438\u043C", -1, 2), new Among("\u044B\u043C", -1, 2), + new Among("\u043D", -1, 1), new Among("\u0435\u043D", 22, 2), + new Among("\u043B\u043E", -1, 1), + new Among("\u0438\u043B\u043E", 24, 2), + new Among("\u044B\u043B\u043E", 24, 2), + new Among("\u043D\u043E", -1, 1), + new Among("\u0435\u043D\u043E", 27, 2), + new Among("\u043D\u043D\u043E", 27, 1), + new Among("\u0435\u0442", -1, 1), + new Among("\u0443\u0435\u0442", 30, 2), + new Among("\u0438\u0442", -1, 2), new Among("\u044B\u0442", -1, 2), + new Among("\u044E\u0442", -1, 1), + new Among("\u0443\u044E\u0442", 34, 2), + new Among("\u044F\u0442", -1, 2), new Among("\u043D\u044B", -1, 1), + new Among("\u0435\u043D\u044B", 37, 2), + new Among("\u0442\u044C", -1, 1), + new Among("\u0438\u0442\u044C", 39, 2), + new Among("\u044B\u0442\u044C", 39, 2), + new Among("\u0435\u0448\u044C", -1, 1), + new Among("\u0438\u0448\u044C", -1, 2), new Among("\u044E", -1, 2), + new Among("\u0443\u044E", 44, 2) + ], + a_5 = [ + new Among("\u0430", -1, 1), new Among("\u0435\u0432", -1, 1), + new Among("\u043E\u0432", -1, 1), new Among("\u0435", -1, 1), + new Among("\u0438\u0435", 3, 1), new Among("\u044C\u0435", 3, 1), + new Among("\u0438", -1, 1), new Among("\u0435\u0438", 6, 1), + new Among("\u0438\u0438", 6, 1), + new Among("\u0430\u043C\u0438", 6, 1), + new Among("\u044F\u043C\u0438", 6, 1), + new Among("\u0438\u044F\u043C\u0438", 10, 1), + new Among("\u0439", -1, 1), new Among("\u0435\u0439", 12, 1), + new Among("\u0438\u0435\u0439", 13, 1), + new Among("\u0438\u0439", 12, 1), new Among("\u043E\u0439", 12, 1), + new Among("\u0430\u043C", -1, 1), new Among("\u0435\u043C", -1, 1), + new Among("\u0438\u0435\u043C", 18, 1), + new Among("\u043E\u043C", -1, 1), new Among("\u044F\u043C", -1, 1), + new Among("\u0438\u044F\u043C", 21, 1), new Among("\u043E", -1, 1), + new Among("\u0443", -1, 1), new Among("\u0430\u0445", -1, 1), + new Among("\u044F\u0445", -1, 1), + new Among("\u0438\u044F\u0445", 26, 1), new Among("\u044B", -1, 1), + new Among("\u044C", -1, 1), new Among("\u044E", -1, 1), + new Among("\u0438\u044E", 30, 1), new Among("\u044C\u044E", 30, 1), + new Among("\u044F", -1, 1), new Among("\u0438\u044F", 33, 1), + new Among("\u044C\u044F", 33, 1) + ], + a_6 = [ + new Among("\u043E\u0441\u0442", -1, 1), + new Among("\u043E\u0441\u0442\u044C", -1, 1) + ], + a_7 = [ + new Among("\u0435\u0439\u0448\u0435", -1, 1), + new Among("\u043D", -1, 2), new Among("\u0435\u0439\u0448", -1, 1), + new Among("\u044C", -1, 3) + ], + g_v = [33, 65, 8, 232], + I_p2, I_pV, sbp = new SnowballProgram(); + this.setCurrent = function(word) { + sbp.setCurrent(word); + }; + this.getCurrent = function() { + return sbp.getCurrent(); + }; + + function habr3() { + while (!sbp.in_grouping(g_v, 1072, 1103)) { + if (sbp.cursor >= sbp.limit) + return false; + sbp.cursor++; + } + return true; + } + + function habr4() { + while (!sbp.out_grouping(g_v, 1072, 1103)) { + if (sbp.cursor >= sbp.limit) + return false; + sbp.cursor++; + } + return true; + } + + function r_mark_regions() { + I_pV = sbp.limit; + I_p2 = I_pV; + if (habr3()) { + I_pV = sbp.cursor; + if (habr4()) + if (habr3()) + if (habr4()) + I_p2 = sbp.cursor; + } + } + + function r_R2() { + return I_p2 <= sbp.cursor; + } + + function habr2(a, n) { + var among_var, v_1; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a, n); + if (among_var) { + sbp.bra = sbp.cursor; + switch (among_var) { + case 1: + v_1 = sbp.limit - sbp.cursor; + if (!sbp.eq_s_b(1, "\u0430")) { + sbp.cursor = sbp.limit - v_1; + if (!sbp.eq_s_b(1, "\u044F")) + return false; + } + case 2: + sbp.slice_del(); + break; + } + return true; + } + return false; + } + + function r_perfective_gerund() { + return habr2(a_0, 9); + } + + function habr1(a, n) { + var among_var; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a, n); + if (among_var) { + sbp.bra = sbp.cursor; + if (among_var == 1) + sbp.slice_del(); + return true; + } + return false; + } + + function r_adjective() { + return habr1(a_1, 26); + } + + function r_adjectival() { + var among_var; + if (r_adjective()) { + habr2(a_2, 8); + return true; + } + return false; + } + + function r_reflexive() { + return habr1(a_3, 2); + } + + function r_verb() { + return habr2(a_4, 46); + } + + function r_noun() { + habr1(a_5, 36); + } + + function r_derivational() { + var among_var; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_6, 2); + if (among_var) { + sbp.bra = sbp.cursor; + if (r_R2() && among_var == 1) + sbp.slice_del(); + } + } + + function r_tidy_up() { + var among_var; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_7, 4); + if (among_var) { + sbp.bra = sbp.cursor; + switch (among_var) { + case 1: + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (!sbp.eq_s_b(1, "\u043D")) + break; + sbp.bra = sbp.cursor; + case 2: + if (!sbp.eq_s_b(1, "\u043D")) + break; + case 3: + sbp.slice_del(); + break; + } + } + } + this.stem = function() { + r_mark_regions(); + sbp.cursor = sbp.limit; + if (sbp.cursor < I_pV) + return false; + sbp.limit_backward = I_pV; + if (!r_perfective_gerund()) { + sbp.cursor = sbp.limit; + if (!r_reflexive()) + sbp.cursor = sbp.limit; + if (!r_adjectival()) { + sbp.cursor = sbp.limit; + if (!r_verb()) { + sbp.cursor = sbp.limit; + r_noun(); + } + } + } + sbp.cursor = sbp.limit; + sbp.ket = sbp.cursor; + if (sbp.eq_s_b(1, "\u0438")) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + } else + sbp.cursor = sbp.limit; + r_derivational(); + sbp.cursor = sbp.limit; + r_tidy_up(); + return true; + } + }; + + /* and return a function that stems a word for the current locale */ + return function(token) { + // for lunr version 2 + if (typeof token.update === "function") { + return token.update(function(word) { + st.setCurrent(word); + st.stem(); + return st.getCurrent(); + }) + } else { // for lunr version <= 1 + st.setCurrent(token); + st.stem(); + return st.getCurrent(); + } + } + })(); + + lunr.Pipeline.registerFunction(lunr.ru.stemmer, 'stemmer-ru'); + + lunr.ru.stopWordFilter = lunr.generateStopWordFilter('алло без близко более больше будем будет будете будешь будто буду будут будь бы бывает бывь был была были было быть в важная важное важные важный вам вами вас ваш ваша ваше ваши вверх вдали вдруг ведь везде весь вниз внизу во вокруг вон восемнадцатый восемнадцать восемь восьмой вот впрочем времени время все всегда всего всем всеми всему всех всею всю всюду вся всё второй вы г где говорил говорит год года году да давно даже далеко дальше даром два двадцатый двадцать две двенадцатый двенадцать двух девятнадцатый девятнадцать девятый девять действительно дел день десятый десять для до довольно долго должно другая другие других друго другое другой е его ее ей ему если есть еще ещё ею её ж же жизнь за занят занята занято заняты затем зато зачем здесь значит и из или им именно иметь ими имя иногда их к каждая каждое каждые каждый кажется как какая какой кем когда кого ком кому конечно которая которого которой которые который которых кроме кругом кто куда лет ли лишь лучше люди м мало между меля менее меньше меня миллионов мимо мира мне много многочисленная многочисленное многочисленные многочисленный мной мною мог могут мож может можно можхо мои мой мор мочь моя моё мы на наверху над надо назад наиболее наконец нам нами нас начала наш наша наше наши не него недавно недалеко нее ней нельзя нем немного нему непрерывно нередко несколько нет нею неё ни нибудь ниже низко никогда никуда ними них ничего но ну нужно нх о об оба обычно один одиннадцатый одиннадцать однажды однако одного одной около он она они оно опять особенно от отовсюду отсюда очень первый перед по под пожалуйста позже пока пор пора после посреди потом потому почему почти прекрасно при про просто против процентов пятнадцатый пятнадцать пятый пять раз разве рано раньше рядом с сам сама сами самим самими самих само самого самой самом самому саму свое своего своей свои своих свою сеаой себе себя сегодня седьмой сейчас семнадцатый семнадцать семь сих сказал сказала сказать сколько слишком сначала снова со собой собою совсем спасибо стал суть т та так такая также такие такое такой там твой твоя твоё те тебе тебя тем теми теперь тех то тобой тобою тогда того тоже только том тому тот тою третий три тринадцатый тринадцать ту туда тут ты тысяч у уж уже уметь хорошо хотеть хоть хотя хочешь часто чаще чего человек чем чему через четвертый четыре четырнадцатый четырнадцать что чтоб чтобы чуть шестнадцатый шестнадцать шестой шесть эта эти этим этими этих это этого этой этом этому этот эту я а'.split(' ')); + + lunr.Pipeline.registerFunction(lunr.ru.stopWordFilter, 'stopWordFilter-ru'); + }; +})) \ No newline at end of file diff --git a/mkdocs/contrib/search/lunr-language/lunr.stemmer.support.js b/mkdocs/contrib/search/lunr-language/lunr.stemmer.support.js new file mode 100644 index 0000000000..896476a181 --- /dev/null +++ b/mkdocs/contrib/search/lunr-language/lunr.stemmer.support.js @@ -0,0 +1,304 @@ +/*! + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +/** + * 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 environments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + factory()(root.lunr); + } +}(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 function(lunr) { + /* provides utilities for the included stemmers */ + lunr.stemmerSupport = { + Among: function(s, substring_i, result, method) { + this.toCharArray = function(s) { + var sLength = s.length, charArr = new Array(sLength); + for (var i = 0; i < sLength; i++) + charArr[i] = s.charCodeAt(i); + return charArr; + }; + + if ((!s && s != "") || (!substring_i && (substring_i != 0)) || !result) + throw ("Bad Among initialisation: s:" + s + ", substring_i: " + + substring_i + ", result: " + result); + this.s_size = s.length; + this.s = this.toCharArray(s); + this.substring_i = substring_i; + this.result = result; + this.method = method; + }, + SnowballProgram: function() { + var current; + return { + bra : 0, + ket : 0, + limit : 0, + cursor : 0, + limit_backward : 0, + setCurrent : function(word) { + current = word; + this.cursor = 0; + this.limit = word.length; + this.limit_backward = 0; + this.bra = this.cursor; + this.ket = this.limit; + }, + getCurrent : function() { + var result = current; + current = null; + return result; + }, + in_grouping : function(s, min, max) { + if (this.cursor < this.limit) { + var ch = current.charCodeAt(this.cursor); + if (ch <= max && ch >= min) { + ch -= min; + if (s[ch >> 3] & (0X1 << (ch & 0X7))) { + this.cursor++; + return true; + } + } + } + return false; + }, + in_grouping_b : function(s, min, max) { + if (this.cursor > this.limit_backward) { + var ch = current.charCodeAt(this.cursor - 1); + if (ch <= max && ch >= min) { + ch -= min; + if (s[ch >> 3] & (0X1 << (ch & 0X7))) { + this.cursor--; + return true; + } + } + } + return false; + }, + out_grouping : function(s, min, max) { + if (this.cursor < this.limit) { + var ch = current.charCodeAt(this.cursor); + if (ch > max || ch < min) { + this.cursor++; + return true; + } + ch -= min; + if (!(s[ch >> 3] & (0X1 << (ch & 0X7)))) { + this.cursor++; + return true; + } + } + return false; + }, + out_grouping_b : function(s, min, max) { + if (this.cursor > this.limit_backward) { + var ch = current.charCodeAt(this.cursor - 1); + if (ch > max || ch < min) { + this.cursor--; + return true; + } + ch -= min; + if (!(s[ch >> 3] & (0X1 << (ch & 0X7)))) { + this.cursor--; + return true; + } + } + return false; + }, + eq_s : function(s_size, s) { + if (this.limit - this.cursor < s_size) + return false; + for (var i = 0; i < s_size; i++) + if (current.charCodeAt(this.cursor + i) != s.charCodeAt(i)) + return false; + this.cursor += s_size; + return true; + }, + eq_s_b : function(s_size, s) { + if (this.cursor - this.limit_backward < s_size) + return false; + for (var i = 0; i < s_size; i++) + if (current.charCodeAt(this.cursor - s_size + i) != s + .charCodeAt(i)) + return false; + this.cursor -= s_size; + return true; + }, + find_among : function(v, v_size) { + var i = 0, j = v_size, c = this.cursor, l = this.limit, common_i = 0, common_j = 0, first_key_inspected = false; + while (true) { + var k = i + ((j - i) >> 1), diff = 0, common = common_i < common_j + ? common_i + : common_j, w = v[k]; + for (var i2 = common; i2 < w.s_size; i2++) { + if (c + common == l) { + diff = -1; + break; + } + diff = current.charCodeAt(c + common) - w.s[i2]; + if (diff) + break; + common++; + } + if (diff < 0) { + j = k; + common_j = common; + } else { + i = k; + common_i = common; + } + if (j - i <= 1) { + if (i > 0 || j == i || first_key_inspected) + break; + first_key_inspected = true; + } + } + while (true) { + var w = v[i]; + if (common_i >= w.s_size) { + this.cursor = c + w.s_size; + if (!w.method) + return w.result; + var res = w.method(); + this.cursor = c + w.s_size; + if (res) + return w.result; + } + i = w.substring_i; + if (i < 0) + return 0; + } + }, + find_among_b : function(v, v_size) { + var i = 0, j = v_size, c = this.cursor, lb = this.limit_backward, common_i = 0, common_j = 0, first_key_inspected = false; + while (true) { + var k = i + ((j - i) >> 1), diff = 0, common = common_i < common_j + ? common_i + : common_j, w = v[k]; + for (var i2 = w.s_size - 1 - common; i2 >= 0; i2--) { + if (c - common == lb) { + diff = -1; + break; + } + diff = current.charCodeAt(c - 1 - common) - w.s[i2]; + if (diff) + break; + common++; + } + if (diff < 0) { + j = k; + common_j = common; + } else { + i = k; + common_i = common; + } + if (j - i <= 1) { + if (i > 0 || j == i || first_key_inspected) + break; + first_key_inspected = true; + } + } + while (true) { + var w = v[i]; + if (common_i >= w.s_size) { + this.cursor = c - w.s_size; + if (!w.method) + return w.result; + var res = w.method(); + this.cursor = c - w.s_size; + if (res) + return w.result; + } + i = w.substring_i; + if (i < 0) + return 0; + } + }, + replace_s : function(c_bra, c_ket, s) { + var adjustment = s.length - (c_ket - c_bra), left = current + .substring(0, c_bra), right = current.substring(c_ket); + current = left + s + right; + this.limit += adjustment; + if (this.cursor >= c_ket) + this.cursor += adjustment; + else if (this.cursor > c_bra) + this.cursor = c_bra; + return adjustment; + }, + slice_check : function() { + if (this.bra < 0 || this.bra > this.ket || this.ket > this.limit + || this.limit > current.length) + throw ("faulty slice operation"); + }, + slice_from : function(s) { + this.slice_check(); + this.replace_s(this.bra, this.ket, s); + }, + slice_del : function() { + this.slice_from(""); + }, + insert : function(c_bra, c_ket, s) { + var adjustment = this.replace_s(c_bra, c_ket, s); + if (c_bra <= this.bra) + this.bra += adjustment; + if (c_bra <= this.ket) + this.ket += adjustment; + }, + slice_to : function() { + this.slice_check(); + return current.substring(this.bra, this.ket); + }, + eq_v_b : function(s) { + return this.eq_s_b(s.length, s); + } + }; + } + }; + + lunr.trimmerSupport = { + generateTrimmer: function(wordCharacters) { + var startRegex = new RegExp("^[^" + wordCharacters + "]+") + var endRegex = new RegExp("[^" + wordCharacters + "]+$") + + return function(token) { + // for lunr version 2 + if (typeof token.update === "function") { + return token.update(function (s) { + return s + .replace(startRegex, '') + .replace(endRegex, ''); + }) + } else { // for lunr version 1 + return token + .replace(startRegex, '') + .replace(endRegex, ''); + } + }; + } + } + } +})); diff --git a/mkdocs/contrib/search/lunr-language/lunr.sv.js b/mkdocs/contrib/search/lunr-language/lunr.sv.js new file mode 100644 index 0000000000..6a7d93202b --- /dev/null +++ b/mkdocs/contrib/search/lunr-language/lunr.sv.js @@ -0,0 +1,256 @@ +/*! + * Lunr languages, `Swedish` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +/** + * 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 environments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + factory()(root.lunr); + } +}(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 function(lunr) { + /* throw error if lunr is not yet included */ + if ('undefined' === typeof lunr) { + throw new Error('Lunr is not present. Please include / require Lunr before this script.'); + } + + /* throw error if lunr stemmer support is not yet included */ + if ('undefined' === typeof lunr.stemmerSupport) { + throw new Error('Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.'); + } + + /* register specific locale function */ + lunr.sv = function() { + this.pipeline.reset(); + this.pipeline.add( + lunr.sv.trimmer, + lunr.sv.stopWordFilter, + lunr.sv.stemmer + ); + + // for lunr version 2 + // this is necessary so that every searched word is also stemmed before + // in lunr <= 1 this is not needed, as it is done using the normal pipeline + if (this.searchPipeline) { + this.searchPipeline.reset(); + this.searchPipeline.add(lunr.sv.stemmer) + } + }; + + /* lunr trimmer function */ + lunr.sv.wordCharacters = "A-Za-z\xAA\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02B8\u02E0-\u02E4\u1D00-\u1D25\u1D2C-\u1D5C\u1D62-\u1D65\u1D6B-\u1D77\u1D79-\u1DBE\u1E00-\u1EFF\u2071\u207F\u2090-\u209C\u212A\u212B\u2132\u214E\u2160-\u2188\u2C60-\u2C7F\uA722-\uA787\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA7FF\uAB30-\uAB5A\uAB5C-\uAB64\uFB00-\uFB06\uFF21-\uFF3A\uFF41-\uFF5A"; + lunr.sv.trimmer = lunr.trimmerSupport.generateTrimmer(lunr.sv.wordCharacters); + + lunr.Pipeline.registerFunction(lunr.sv.trimmer, 'trimmer-sv'); + + /* lunr stemmer function */ + lunr.sv.stemmer = (function() { + /* create the wrapped stemmer object */ + var Among = lunr.stemmerSupport.Among, + SnowballProgram = lunr.stemmerSupport.SnowballProgram, + st = new function SwedishStemmer() { + var a_0 = [new Among("a", -1, 1), new Among("arna", 0, 1), + new Among("erna", 0, 1), new Among("heterna", 2, 1), + new Among("orna", 0, 1), new Among("ad", -1, 1), + new Among("e", -1, 1), new Among("ade", 6, 1), + new Among("ande", 6, 1), new Among("arne", 6, 1), + new Among("are", 6, 1), new Among("aste", 6, 1), + new Among("en", -1, 1), new Among("anden", 12, 1), + new Among("aren", 12, 1), new Among("heten", 12, 1), + new Among("ern", -1, 1), new Among("ar", -1, 1), + new Among("er", -1, 1), new Among("heter", 18, 1), + new Among("or", -1, 1), new Among("s", -1, 2), + new Among("as", 21, 1), new Among("arnas", 22, 1), + new Among("ernas", 22, 1), new Among("ornas", 22, 1), + new Among("es", 21, 1), new Among("ades", 26, 1), + new Among("andes", 26, 1), new Among("ens", 21, 1), + new Among("arens", 29, 1), new Among("hetens", 29, 1), + new Among("erns", 21, 1), new Among("at", -1, 1), + new Among("andet", -1, 1), new Among("het", -1, 1), + new Among("ast", -1, 1) + ], + a_1 = [new Among("dd", -1, -1), + new Among("gd", -1, -1), new Among("nn", -1, -1), + new Among("dt", -1, -1), new Among("gt", -1, -1), + new Among("kt", -1, -1), new Among("tt", -1, -1) + ], + a_2 = [ + new Among("ig", -1, 1), new Among("lig", 0, 1), + new Among("els", -1, 1), new Among("fullt", -1, 3), + new Among("l\u00F6st", -1, 2) + ], + g_v = [17, 65, 16, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 32 + ], + g_s_ending = [119, 127, 149], + I_x, I_p1, sbp = new SnowballProgram(); + this.setCurrent = function(word) { + sbp.setCurrent(word); + }; + this.getCurrent = function() { + return sbp.getCurrent(); + }; + + function r_mark_regions() { + var v_1, c = sbp.cursor + 3; + I_p1 = sbp.limit; + if (0 <= c || c <= sbp.limit) { + I_x = c; + while (true) { + v_1 = sbp.cursor; + if (sbp.in_grouping(g_v, 97, 246)) { + sbp.cursor = v_1; + break; + } + sbp.cursor = v_1; + if (sbp.cursor >= sbp.limit) + return; + sbp.cursor++; + } + while (!sbp.out_grouping(g_v, 97, 246)) { + if (sbp.cursor >= sbp.limit) + return; + sbp.cursor++; + } + I_p1 = sbp.cursor; + if (I_p1 < I_x) + I_p1 = I_x; + } + } + + function r_main_suffix() { + var among_var, v_2 = sbp.limit_backward; + if (sbp.cursor >= I_p1) { + sbp.limit_backward = I_p1; + sbp.cursor = sbp.limit; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_0, 37); + sbp.limit_backward = v_2; + if (among_var) { + sbp.bra = sbp.cursor; + switch (among_var) { + case 1: + sbp.slice_del(); + break; + case 2: + if (sbp.in_grouping_b(g_s_ending, 98, 121)) + sbp.slice_del(); + break; + } + } + } + } + + function r_consonant_pair() { + var v_1 = sbp.limit_backward; + if (sbp.cursor >= I_p1) { + sbp.limit_backward = I_p1; + sbp.cursor = sbp.limit; + if (sbp.find_among_b(a_1, 7)) { + sbp.cursor = sbp.limit; + sbp.ket = sbp.cursor; + if (sbp.cursor > sbp.limit_backward) { + sbp.bra = --sbp.cursor; + sbp.slice_del(); + } + } + sbp.limit_backward = v_1; + } + } + + function r_other_suffix() { + var among_var, v_2; + if (sbp.cursor >= I_p1) { + v_2 = sbp.limit_backward; + sbp.limit_backward = I_p1; + sbp.cursor = sbp.limit; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_2, 5); + if (among_var) { + sbp.bra = sbp.cursor; + switch (among_var) { + case 1: + sbp.slice_del(); + break; + case 2: + sbp.slice_from("l\u00F6s"); + break; + case 3: + sbp.slice_from("full"); + break; + } + } + sbp.limit_backward = v_2; + } + } + this.stem = function() { + var v_1 = sbp.cursor; + r_mark_regions(); + sbp.limit_backward = v_1; + sbp.cursor = sbp.limit; + r_main_suffix(); + sbp.cursor = sbp.limit; + r_consonant_pair(); + sbp.cursor = sbp.limit; + r_other_suffix(); + return true; + } + }; + + /* and return a function that stems a word for the current locale */ + return function(token) { + // for lunr version 2 + if (typeof token.update === "function") { + return token.update(function(word) { + st.setCurrent(word); + st.stem(); + return st.getCurrent(); + }) + } else { // for lunr version <= 1 + st.setCurrent(token); + st.stem(); + return st.getCurrent(); + } + } + })(); + + lunr.Pipeline.registerFunction(lunr.sv.stemmer, 'stemmer-sv'); + + lunr.sv.stopWordFilter = lunr.generateStopWordFilter('alla allt att av blev bli blir blivit de dem den denna deras dess dessa det detta dig din dina ditt du där då efter ej eller en er era ert ett från för ha hade han hans har henne hennes hon honom hur här i icke ingen inom inte jag ju kan kunde man med mellan men mig min mina mitt mot mycket ni nu när någon något några och om oss på samma sedan sig sin sina sitta själv skulle som så sådan sådana sådant till under upp ut utan vad var vara varför varit varje vars vart vem vi vid vilka vilkas vilken vilket vår våra vårt än är åt över'.split(' ')); + + lunr.Pipeline.registerFunction(lunr.sv.stopWordFilter, 'stopWordFilter-sv'); + }; +})) \ No newline at end of file diff --git a/mkdocs/contrib/search/lunr-language/lunr.th.js b/mkdocs/contrib/search/lunr-language/lunr.th.js new file mode 100644 index 0000000000..2bf7db519a --- /dev/null +++ b/mkdocs/contrib/search/lunr-language/lunr.th.js @@ -0,0 +1,97 @@ +/*! + * Lunr languages, `Thai` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2017, Keerati Thiwanruk + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +/** + * 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 environments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + factory()(root.lunr); + } +}(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 function(lunr) { + /* throw error if lunr is not yet included */ + if ('undefined' === typeof lunr) { + throw new Error('Lunr is not present. Please include / require Lunr before this script.'); + } + + /* throw error if lunr stemmer support is not yet included */ + if ('undefined' === typeof lunr.stemmerSupport) { + throw new Error('Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.'); + } + + /* + Thai tokenization is the same to Japanense, which does not take into account spaces. + So, it uses the same logic to assign tokenization function due to different Lunr versions. + */ + var isLunr2 = lunr.version[0] == "2"; + + /* register specific locale function */ + lunr.th = function() { + this.pipeline.reset(); + this.pipeline.add( + /*lunr.th.stopWordFilter,*/ + lunr.th.trimmer + ); + + if (isLunr2) { // for lunr version 2.0.0 + this.tokenizer = lunr.th.tokenizer; + } else { + if (lunr.tokenizer) { // for lunr version 0.6.0 + lunr.tokenizer = lunr.th.tokenizer; + } + if (this.tokenizerFn) { // for lunr version 0.7.0 -> 1.0.0 + this.tokenizerFn = lunr.th.tokenizer; + } + } + }; + + /* lunr trimmer function */ + lunr.th.wordCharacters = "[\u0e00-\u0e7f]"; + lunr.th.trimmer = lunr.trimmerSupport.generateTrimmer(lunr.th.wordCharacters); + lunr.Pipeline.registerFunction(lunr.th.trimmer, 'trimmer-th'); + + var segmenter = lunr.wordcut; + segmenter.init(); + lunr.th.tokenizer = function (obj) { + //console.log(obj); + if (!arguments.length || obj == null || obj == undefined) return [] + if (Array.isArray(obj)) return obj.map(function (t) { return isLunr2 ? new lunr.Token(t) : t }) + + var str = obj.toString().replace(/^\s+/, ''); + return segmenter.cut(str).split('|'); + } + }; +})) diff --git a/mkdocs/contrib/search/lunr-language/lunr.tr.js b/mkdocs/contrib/search/lunr-language/lunr.tr.js new file mode 100644 index 0000000000..5b4bad814c --- /dev/null +++ b/mkdocs/contrib/search/lunr-language/lunr.tr.js @@ -0,0 +1,1087 @@ +/*! + * Lunr languages, `Turkish` language + * https://github.com/MihaiValentin/lunr-languages + * + * Copyright 2014, Mihai Valentin + * http://www.mozilla.org/MPL/ + */ +/*! + * based on + * Snowball JavaScript Library v0.3 + * http://code.google.com/p/urim/ + * http://snowball.tartarus.org/ + * + * Copyright 2010, Oleg Mazko + * http://www.mozilla.org/MPL/ + */ + +/** + * 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 environments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + factory()(root.lunr); + } +}(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 function(lunr) { + /* throw error if lunr is not yet included */ + if ('undefined' === typeof lunr) { + throw new Error('Lunr is not present. Please include / require Lunr before this script.'); + } + + /* throw error if lunr stemmer support is not yet included */ + if ('undefined' === typeof lunr.stemmerSupport) { + throw new Error('Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.'); + } + + /* register specific locale function */ + lunr.tr = function() { + this.pipeline.reset(); + this.pipeline.add( + lunr.tr.trimmer, + lunr.tr.stopWordFilter, + lunr.tr.stemmer + ); + + // for lunr version 2 + // this is necessary so that every searched word is also stemmed before + // in lunr <= 1 this is not needed, as it is done using the normal pipeline + if (this.searchPipeline) { + this.searchPipeline.reset(); + this.searchPipeline.add(lunr.tr.stemmer) + } + }; + + /* lunr trimmer function */ + lunr.tr.wordCharacters = "A-Za-z\xAA\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02B8\u02E0-\u02E4\u1D00-\u1D25\u1D2C-\u1D5C\u1D62-\u1D65\u1D6B-\u1D77\u1D79-\u1DBE\u1E00-\u1EFF\u2071\u207F\u2090-\u209C\u212A\u212B\u2132\u214E\u2160-\u2188\u2C60-\u2C7F\uA722-\uA787\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA7FF\uAB30-\uAB5A\uAB5C-\uAB64\uFB00-\uFB06\uFF21-\uFF3A\uFF41-\uFF5A"; + lunr.tr.trimmer = lunr.trimmerSupport.generateTrimmer(lunr.tr.wordCharacters); + + lunr.Pipeline.registerFunction(lunr.tr.trimmer, 'trimmer-tr'); + + /* lunr stemmer function */ + lunr.tr.stemmer = (function() { + /* create the wrapped stemmer object */ + var Among = lunr.stemmerSupport.Among, + SnowballProgram = lunr.stemmerSupport.SnowballProgram, + st = new function TurkishStemmer() { + var a_0 = [new Among("m", -1, -1), new Among("n", -1, -1), + new Among("miz", -1, -1), new Among("niz", -1, -1), + new Among("muz", -1, -1), new Among("nuz", -1, -1), + new Among("m\u00FCz", -1, -1), new Among("n\u00FCz", -1, -1), + new Among("m\u0131z", -1, -1), new Among("n\u0131z", -1, -1) + ], + a_1 = [ + new Among("leri", -1, -1), new Among("lar\u0131", -1, -1) + ], + a_2 = [ + new Among("ni", -1, -1), new Among("nu", -1, -1), + new Among("n\u00FC", -1, -1), new Among("n\u0131", -1, -1) + ], + a_3 = [ + new Among("in", -1, -1), new Among("un", -1, -1), + new Among("\u00FCn", -1, -1), new Among("\u0131n", -1, -1) + ], + a_4 = [ + new Among("a", -1, -1), new Among("e", -1, -1) + ], + a_5 = [ + new Among("na", -1, -1), new Among("ne", -1, -1) + ], + a_6 = [ + new Among("da", -1, -1), new Among("ta", -1, -1), + new Among("de", -1, -1), new Among("te", -1, -1) + ], + a_7 = [ + new Among("nda", -1, -1), new Among("nde", -1, -1) + ], + a_8 = [ + new Among("dan", -1, -1), new Among("tan", -1, -1), + new Among("den", -1, -1), new Among("ten", -1, -1) + ], + a_9 = [ + new Among("ndan", -1, -1), new Among("nden", -1, -1) + ], + a_10 = [ + new Among("la", -1, -1), new Among("le", -1, -1) + ], + a_11 = [ + new Among("ca", -1, -1), new Among("ce", -1, -1) + ], + a_12 = [ + new Among("im", -1, -1), new Among("um", -1, -1), + new Among("\u00FCm", -1, -1), new Among("\u0131m", -1, -1) + ], + a_13 = [ + new Among("sin", -1, -1), new Among("sun", -1, -1), + new Among("s\u00FCn", -1, -1), new Among("s\u0131n", -1, -1) + ], + a_14 = [ + new Among("iz", -1, -1), new Among("uz", -1, -1), + new Among("\u00FCz", -1, -1), new Among("\u0131z", -1, -1) + ], + a_15 = [ + new Among("siniz", -1, -1), new Among("sunuz", -1, -1), + new Among("s\u00FCn\u00FCz", -1, -1), + new Among("s\u0131n\u0131z", -1, -1) + ], + a_16 = [ + new Among("lar", -1, -1), new Among("ler", -1, -1) + ], + a_17 = [ + new Among("niz", -1, -1), new Among("nuz", -1, -1), + new Among("n\u00FCz", -1, -1), new Among("n\u0131z", -1, -1) + ], + a_18 = [ + new Among("dir", -1, -1), new Among("tir", -1, -1), + new Among("dur", -1, -1), new Among("tur", -1, -1), + new Among("d\u00FCr", -1, -1), new Among("t\u00FCr", -1, -1), + new Among("d\u0131r", -1, -1), new Among("t\u0131r", -1, -1) + ], + a_19 = [ + new Among("cas\u0131na", -1, -1), new Among("cesine", -1, -1) + ], + a_20 = [ + new Among("di", -1, -1), new Among("ti", -1, -1), + new Among("dik", -1, -1), new Among("tik", -1, -1), + new Among("duk", -1, -1), new Among("tuk", -1, -1), + new Among("d\u00FCk", -1, -1), new Among("t\u00FCk", -1, -1), + new Among("d\u0131k", -1, -1), new Among("t\u0131k", -1, -1), + new Among("dim", -1, -1), new Among("tim", -1, -1), + new Among("dum", -1, -1), new Among("tum", -1, -1), + new Among("d\u00FCm", -1, -1), new Among("t\u00FCm", -1, -1), + new Among("d\u0131m", -1, -1), new Among("t\u0131m", -1, -1), + new Among("din", -1, -1), new Among("tin", -1, -1), + new Among("dun", -1, -1), new Among("tun", -1, -1), + new Among("d\u00FCn", -1, -1), new Among("t\u00FCn", -1, -1), + new Among("d\u0131n", -1, -1), new Among("t\u0131n", -1, -1), + new Among("du", -1, -1), new Among("tu", -1, -1), + new Among("d\u00FC", -1, -1), new Among("t\u00FC", -1, -1), + new Among("d\u0131", -1, -1), new Among("t\u0131", -1, -1) + ], + a_21 = [ + new Among("sa", -1, -1), new Among("se", -1, -1), + new Among("sak", -1, -1), new Among("sek", -1, -1), + new Among("sam", -1, -1), new Among("sem", -1, -1), + new Among("san", -1, -1), new Among("sen", -1, -1) + ], + a_22 = [ + new Among("mi\u015F", -1, -1), new Among("mu\u015F", -1, -1), + new Among("m\u00FC\u015F", -1, -1), + new Among("m\u0131\u015F", -1, -1) + ], + a_23 = [new Among("b", -1, 1), + new Among("c", -1, 2), new Among("d", -1, 3), + new Among("\u011F", -1, 4) + ], + g_vowel = [17, 65, 16, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 8, 0, 0, 0, 0, 0, 0, 1 + ], + g_U = [ + 1, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, + 0, 0, 0, 1 + ], + g_vowel1 = [1, 64, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 + ], + g_vowel2 = [17, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130 + ], + g_vowel3 = [1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1 + ], + g_vowel4 = [17], + g_vowel5 = [65], + g_vowel6 = [65], + B_c_s_n_s, I_strlen, g_habr = [ + ["a", g_vowel1, 97, 305], + ["e", g_vowel2, 101, 252], + ["\u0131", g_vowel3, 97, 305], + ["i", g_vowel4, 101, 105], + ["o", g_vowel5, 111, 117], + ["\u00F6", g_vowel6, 246, 252], + ["u", g_vowel5, 111, 117] + ], + sbp = new SnowballProgram(); + this.setCurrent = function(word) { + sbp.setCurrent(word); + }; + this.getCurrent = function() { + return sbp.getCurrent(); + }; + + function habr1(g_v, n1, n2) { + while (true) { + var v_1 = sbp.limit - sbp.cursor; + if (sbp.in_grouping_b(g_v, n1, n2)) { + sbp.cursor = sbp.limit - v_1; + break; + } + sbp.cursor = sbp.limit - v_1; + if (sbp.cursor <= sbp.limit_backward) + return false; + sbp.cursor--; + } + return true; + } + + function r_check_vowel_harmony() { + var v_1, v_2; + v_1 = sbp.limit - sbp.cursor; + habr1(g_vowel, 97, 305); + for (var i = 0; i < g_habr.length; i++) { + v_2 = sbp.limit - sbp.cursor; + var habr = g_habr[i]; + if (sbp.eq_s_b(1, habr[0]) && habr1(habr[1], habr[2], habr[3])) { + sbp.cursor = sbp.limit - v_1; + return true; + } + sbp.cursor = sbp.limit - v_2; + } + sbp.cursor = sbp.limit - v_2; + if (!sbp.eq_s_b(1, "\u00FC") || !habr1(g_vowel6, 246, 252)) + return false; + sbp.cursor = sbp.limit - v_1; + return true; + } + + function habr2(f1, f2) { + var v_1 = sbp.limit - sbp.cursor, + v_2; + if (f1()) { + sbp.cursor = sbp.limit - v_1; + if (sbp.cursor > sbp.limit_backward) { + sbp.cursor--; + v_2 = sbp.limit - sbp.cursor; + if (f2()) { + sbp.cursor = sbp.limit - v_2; + return true; + } + } + } + sbp.cursor = sbp.limit - v_1; + if (f1()) { + sbp.cursor = sbp.limit - v_1; + return false; + } + sbp.cursor = sbp.limit - v_1; + if (sbp.cursor <= sbp.limit_backward) + return false; + sbp.cursor--; + if (!f2()) + return false; + sbp.cursor = sbp.limit - v_1; + return true; + } + + function habr3(f1) { + return habr2(f1, function() { + return sbp.in_grouping_b(g_vowel, 97, 305); + }); + } + + function r_mark_suffix_with_optional_n_consonant() { + return habr3(function() { + return sbp.eq_s_b(1, "n"); + }); + } + + function r_mark_suffix_with_optional_s_consonant() { + return habr3(function() { + return sbp.eq_s_b(1, "s"); + }); + } + + function r_mark_suffix_with_optional_y_consonant() { + return habr3(function() { + return sbp.eq_s_b(1, "y"); + }); + } + + function r_mark_suffix_with_optional_U_vowel() { + return habr2(function() { + return sbp.in_grouping_b(g_U, 105, 305); + }, function() { + return sbp.out_grouping_b(g_vowel, 97, 305); + }); + } + + function r_mark_possessives() { + return sbp.find_among_b(a_0, 10) && + r_mark_suffix_with_optional_U_vowel(); + } + + function r_mark_sU() { + return r_check_vowel_harmony() && sbp.in_grouping_b(g_U, 105, 305) && + r_mark_suffix_with_optional_s_consonant(); + } + + function r_mark_lArI() { + return sbp.find_among_b(a_1, 2); + } + + function r_mark_yU() { + return r_check_vowel_harmony() && sbp.in_grouping_b(g_U, 105, 305) && + r_mark_suffix_with_optional_y_consonant(); + } + + function r_mark_nU() { + return r_check_vowel_harmony() && sbp.find_among_b(a_2, 4); + } + + function r_mark_nUn() { + return r_check_vowel_harmony() && sbp.find_among_b(a_3, 4) && + r_mark_suffix_with_optional_n_consonant(); + } + + function r_mark_yA() { + return r_check_vowel_harmony() && sbp.find_among_b(a_4, 2) && + r_mark_suffix_with_optional_y_consonant(); + } + + function r_mark_nA() { + return r_check_vowel_harmony() && sbp.find_among_b(a_5, 2); + } + + function r_mark_DA() { + return r_check_vowel_harmony() && sbp.find_among_b(a_6, 4); + } + + function r_mark_ndA() { + return r_check_vowel_harmony() && sbp.find_among_b(a_7, 2); + } + + function r_mark_DAn() { + return r_check_vowel_harmony() && sbp.find_among_b(a_8, 4); + } + + function r_mark_ndAn() { + return r_check_vowel_harmony() && sbp.find_among_b(a_9, 2); + } + + function r_mark_ylA() { + return r_check_vowel_harmony() && sbp.find_among_b(a_10, 2) && + r_mark_suffix_with_optional_y_consonant(); + } + + function r_mark_ki() { + return sbp.eq_s_b(2, "ki"); + } + + function r_mark_ncA() { + return r_check_vowel_harmony() && sbp.find_among_b(a_11, 2) && + r_mark_suffix_with_optional_n_consonant(); + } + + function r_mark_yUm() { + return r_check_vowel_harmony() && sbp.find_among_b(a_12, 4) && + r_mark_suffix_with_optional_y_consonant(); + } + + function r_mark_sUn() { + return r_check_vowel_harmony() && sbp.find_among_b(a_13, 4); + } + + function r_mark_yUz() { + return r_check_vowel_harmony() && sbp.find_among_b(a_14, 4) && + r_mark_suffix_with_optional_y_consonant(); + } + + function r_mark_sUnUz() { + return sbp.find_among_b(a_15, 4); + } + + function r_mark_lAr() { + return r_check_vowel_harmony() && sbp.find_among_b(a_16, 2); + } + + function r_mark_nUz() { + return r_check_vowel_harmony() && sbp.find_among_b(a_17, 4); + } + + function r_mark_DUr() { + return r_check_vowel_harmony() && sbp.find_among_b(a_18, 8); + } + + function r_mark_cAsInA() { + return sbp.find_among_b(a_19, 2); + } + + function r_mark_yDU() { + return r_check_vowel_harmony() && sbp.find_among_b(a_20, 32) && + r_mark_suffix_with_optional_y_consonant(); + } + + function r_mark_ysA() { + return sbp.find_among_b(a_21, 8) && + r_mark_suffix_with_optional_y_consonant(); + } + + function r_mark_ymUs_() { + return r_check_vowel_harmony() && sbp.find_among_b(a_22, 4) && + r_mark_suffix_with_optional_y_consonant(); + } + + function r_mark_yken() { + return sbp.eq_s_b(3, "ken") && + r_mark_suffix_with_optional_y_consonant(); + } + + function habr4() { + var v_1 = sbp.limit - sbp.cursor; + if (!r_mark_ymUs_()) { + sbp.cursor = sbp.limit - v_1; + if (!r_mark_yDU()) { + sbp.cursor = sbp.limit - v_1; + if (!r_mark_ysA()) { + sbp.cursor = sbp.limit - v_1; + if (!r_mark_yken()) + return true; + } + } + } + return false; + } + + function habr5() { + if (r_mark_cAsInA()) { + var v_1 = sbp.limit - sbp.cursor; + if (!r_mark_sUnUz()) { + sbp.cursor = sbp.limit - v_1; + if (!r_mark_lAr()) { + sbp.cursor = sbp.limit - v_1; + if (!r_mark_yUm()) { + sbp.cursor = sbp.limit - v_1; + if (!r_mark_sUn()) { + sbp.cursor = sbp.limit - v_1; + if (!r_mark_yUz()) + sbp.cursor = sbp.limit - v_1; + } + } + } + } + if (r_mark_ymUs_()) + return false; + } + return true; + } + + function habr6() { + if (r_mark_lAr()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + var v_1 = sbp.limit - sbp.cursor; + sbp.ket = sbp.cursor; + if (!r_mark_DUr()) { + sbp.cursor = sbp.limit - v_1; + if (!r_mark_yDU()) { + sbp.cursor = sbp.limit - v_1; + if (!r_mark_ysA()) { + sbp.cursor = sbp.limit - v_1; + if (!r_mark_ymUs_()) + sbp.cursor = sbp.limit - v_1; + } + } + } + B_c_s_n_s = false; + return false; + } + return true; + } + + function habr7() { + if (!r_mark_nUz()) + return true; + var v_1 = sbp.limit - sbp.cursor; + if (!r_mark_yDU()) { + sbp.cursor = sbp.limit - v_1; + if (!r_mark_ysA()) + return true; + } + return false; + } + + function habr8() { + var v_1 = sbp.limit - sbp.cursor, + v_2; + if (!r_mark_sUnUz()) { + sbp.cursor = sbp.limit - v_1; + if (!r_mark_yUz()) { + sbp.cursor = sbp.limit - v_1; + if (!r_mark_sUn()) { + sbp.cursor = sbp.limit - v_1; + if (!r_mark_yUm()) + return true; + } + } + } + sbp.bra = sbp.cursor; + sbp.slice_del(); + v_2 = sbp.limit - sbp.cursor; + sbp.ket = sbp.cursor; + if (!r_mark_ymUs_()) + sbp.cursor = sbp.limit - v_2; + return false; + } + + function r_stem_nominal_verb_suffixes() { + var v_1 = sbp.limit - sbp.cursor, + v_2; + sbp.ket = sbp.cursor; + B_c_s_n_s = true; + if (habr4()) { + sbp.cursor = sbp.limit - v_1; + if (habr5()) { + sbp.cursor = sbp.limit - v_1; + if (habr6()) { + sbp.cursor = sbp.limit - v_1; + if (habr7()) { + sbp.cursor = sbp.limit - v_1; + if (habr8()) { + sbp.cursor = sbp.limit - v_1; + if (!r_mark_DUr()) + return; + sbp.bra = sbp.cursor; + sbp.slice_del(); + sbp.ket = sbp.cursor; + v_2 = sbp.limit - sbp.cursor; + if (!r_mark_sUnUz()) { + sbp.cursor = sbp.limit - v_2; + if (!r_mark_lAr()) { + sbp.cursor = sbp.limit - v_2; + if (!r_mark_yUm()) { + sbp.cursor = sbp.limit - v_2; + if (!r_mark_sUn()) { + sbp.cursor = sbp.limit - v_2; + if (!r_mark_yUz()) + sbp.cursor = sbp.limit - v_2; + } + } + } + } + if (!r_mark_ymUs_()) + sbp.cursor = sbp.limit - v_2; + } + } + } + } + } + sbp.bra = sbp.cursor; + sbp.slice_del(); + } + + function r_stem_suffix_chain_before_ki() { + var v_1, v_2, v_3, v_4; + sbp.ket = sbp.cursor; + if (r_mark_ki()) { + v_1 = sbp.limit - sbp.cursor; + if (r_mark_DA()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + v_2 = sbp.limit - sbp.cursor; + sbp.ket = sbp.cursor; + if (r_mark_lAr()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + r_stem_suffix_chain_before_ki(); + } else { + sbp.cursor = sbp.limit - v_2; + if (r_mark_possessives()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (r_mark_lAr()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + r_stem_suffix_chain_before_ki(); + } + } + } + return true; + } + sbp.cursor = sbp.limit - v_1; + if (r_mark_nUn()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + sbp.ket = sbp.cursor; + v_3 = sbp.limit - sbp.cursor; + if (r_mark_lArI()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + } else { + sbp.cursor = sbp.limit - v_3; + sbp.ket = sbp.cursor; + if (!r_mark_possessives()) { + sbp.cursor = sbp.limit - v_3; + if (!r_mark_sU()) { + sbp.cursor = sbp.limit - v_3; + if (!r_stem_suffix_chain_before_ki()) + return true; + } + } + sbp.bra = sbp.cursor; + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (r_mark_lAr()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + r_stem_suffix_chain_before_ki() + } + } + return true; + } + sbp.cursor = sbp.limit - v_1; + if (r_mark_ndA()) { + v_4 = sbp.limit - sbp.cursor; + if (r_mark_lArI()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + } else { + sbp.cursor = sbp.limit - v_4; + if (r_mark_sU()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (r_mark_lAr()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + r_stem_suffix_chain_before_ki(); + } + } else { + sbp.cursor = sbp.limit - v_4; + if (!r_stem_suffix_chain_before_ki()) + return false; + } + } + return true; + } + } + return false; + } + + function habr9(v_1) { + sbp.ket = sbp.cursor; + if (!r_mark_ndA()) { + sbp.cursor = sbp.limit - v_1; + if (!r_mark_nA()) + return false; + } + var v_2 = sbp.limit - sbp.cursor; + if (r_mark_lArI()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + } else { + sbp.cursor = sbp.limit - v_2; + if (r_mark_sU()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (r_mark_lAr()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + r_stem_suffix_chain_before_ki(); + } + } else { + sbp.cursor = sbp.limit - v_2; + if (!r_stem_suffix_chain_before_ki()) + return false; + } + } + return true; + } + + function habr10(v_1) { + sbp.ket = sbp.cursor; + if (!r_mark_ndAn()) { + sbp.cursor = sbp.limit - v_1; + if (!r_mark_nU()) + return false; + } + var v_2 = sbp.limit - sbp.cursor; + if (!r_mark_sU()) { + sbp.cursor = sbp.limit - v_2; + if (!r_mark_lArI()) + return false; + } + sbp.bra = sbp.cursor; + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (r_mark_lAr()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + r_stem_suffix_chain_before_ki(); + } + return true; + } + + function habr11() { + var v_1 = sbp.limit - sbp.cursor, + v_2; + sbp.ket = sbp.cursor; + if (!r_mark_nUn()) { + sbp.cursor = sbp.limit - v_1; + if (!r_mark_ylA()) + return false; + } + sbp.bra = sbp.cursor; + sbp.slice_del(); + v_2 = sbp.limit - sbp.cursor; + sbp.ket = sbp.cursor; + if (r_mark_lAr()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + if (r_stem_suffix_chain_before_ki()) + return true; + } + sbp.cursor = sbp.limit - v_2; + sbp.ket = sbp.cursor; + if (!r_mark_possessives()) { + sbp.cursor = sbp.limit - v_2; + if (!r_mark_sU()) { + sbp.cursor = sbp.limit - v_2; + if (!r_stem_suffix_chain_before_ki()) + return true; + } + } + sbp.bra = sbp.cursor; + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (r_mark_lAr()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + r_stem_suffix_chain_before_ki(); + } + return true; + } + + function habr12() { + var v_1 = sbp.limit - sbp.cursor, + v_2, v_3; + sbp.ket = sbp.cursor; + if (!r_mark_DA()) { + sbp.cursor = sbp.limit - v_1; + if (!r_mark_yU()) { + sbp.cursor = sbp.limit - v_1; + if (!r_mark_yA()) + return false; + } + } + sbp.bra = sbp.cursor; + sbp.slice_del(); + sbp.ket = sbp.cursor; + v_2 = sbp.limit - sbp.cursor; + if (r_mark_possessives()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + v_3 = sbp.limit - sbp.cursor; + sbp.ket = sbp.cursor; + if (!r_mark_lAr()) + sbp.cursor = sbp.limit - v_3; + } else { + sbp.cursor = sbp.limit - v_2; + if (!r_mark_lAr()) + return true; + } + sbp.bra = sbp.cursor; + sbp.slice_del(); + sbp.ket = sbp.cursor; + r_stem_suffix_chain_before_ki(); + return true; + } + + function r_stem_noun_suffixes() { + var v_1 = sbp.limit - sbp.cursor, + v_2, v_3; + sbp.ket = sbp.cursor; + if (r_mark_lAr()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + r_stem_suffix_chain_before_ki(); + return; + } + sbp.cursor = sbp.limit - v_1; + sbp.ket = sbp.cursor; + if (r_mark_ncA()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + v_2 = sbp.limit - sbp.cursor; + sbp.ket = sbp.cursor; + if (r_mark_lArI()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + } else { + sbp.cursor = sbp.limit - v_2; + sbp.ket = sbp.cursor; + if (!r_mark_possessives()) { + sbp.cursor = sbp.limit - v_2; + if (!r_mark_sU()) { + sbp.cursor = sbp.limit - v_2; + sbp.ket = sbp.cursor; + if (!r_mark_lAr()) + return; + sbp.bra = sbp.cursor; + sbp.slice_del(); + if (!r_stem_suffix_chain_before_ki()) + return; + } + } + sbp.bra = sbp.cursor; + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (r_mark_lAr()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + r_stem_suffix_chain_before_ki(); + } + } + return; + } + sbp.cursor = sbp.limit - v_1; + if (habr9(v_1)) + return; + sbp.cursor = sbp.limit - v_1; + if (habr10(v_1)) + return; + sbp.cursor = sbp.limit - v_1; + sbp.ket = sbp.cursor; + if (r_mark_DAn()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + sbp.ket = sbp.cursor; + v_3 = sbp.limit - sbp.cursor; + if (r_mark_possessives()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (r_mark_lAr()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + r_stem_suffix_chain_before_ki(); + } + } else { + sbp.cursor = sbp.limit - v_3; + if (r_mark_lAr()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + r_stem_suffix_chain_before_ki(); + } else { + sbp.cursor = sbp.limit - v_3; + r_stem_suffix_chain_before_ki(); + } + } + return; + } + sbp.cursor = sbp.limit - v_1; + if (habr11()) + return; + sbp.cursor = sbp.limit - v_1; + if (r_mark_lArI()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + return; + } + sbp.cursor = sbp.limit - v_1; + if (r_stem_suffix_chain_before_ki()) + return; + sbp.cursor = sbp.limit - v_1; + if (habr12()) + return; + sbp.cursor = sbp.limit - v_1; + sbp.ket = sbp.cursor; + if (!r_mark_possessives()) { + sbp.cursor = sbp.limit - v_1; + if (!r_mark_sU()) + return; + } + sbp.bra = sbp.cursor; + sbp.slice_del(); + sbp.ket = sbp.cursor; + if (r_mark_lAr()) { + sbp.bra = sbp.cursor; + sbp.slice_del(); + r_stem_suffix_chain_before_ki(); + } + } + + function r_post_process_last_consonants() { + var among_var; + sbp.ket = sbp.cursor; + among_var = sbp.find_among_b(a_23, 4); + if (among_var) { + sbp.bra = sbp.cursor; + switch (among_var) { + case 1: + sbp.slice_from("p"); + break; + case 2: + sbp.slice_from("\u00E7"); + break; + case 3: + sbp.slice_from("t"); + break; + case 4: + sbp.slice_from("k"); + break; + } + } + } + + function habr13() { + while (true) { + var v_1 = sbp.limit - sbp.cursor; + if (sbp.in_grouping_b(g_vowel, 97, 305)) { + sbp.cursor = sbp.limit - v_1; + break; + } + sbp.cursor = sbp.limit - v_1; + if (sbp.cursor <= sbp.limit_backward) + return false; + sbp.cursor--; + } + return true; + } + + function habr14(v_1, c1, c2) { + sbp.cursor = sbp.limit - v_1; + if (habr13()) { + var v_2 = sbp.limit - sbp.cursor; + if (!sbp.eq_s_b(1, c1)) { + sbp.cursor = sbp.limit - v_2; + if (!sbp.eq_s_b(1, c2)) + return true; + } + sbp.cursor = sbp.limit - v_1; + var c = sbp.cursor; + sbp.insert(sbp.cursor, sbp.cursor, c2); + sbp.cursor = c; + return false; + } + return true; + } + + function r_append_U_to_stems_ending_with_d_or_g() { + var v_1 = sbp.limit - sbp.cursor; + if (!sbp.eq_s_b(1, "d")) { + sbp.cursor = sbp.limit - v_1; + if (!sbp.eq_s_b(1, "g")) + return; + } + if (habr14(v_1, "a", "\u0131")) + if (habr14(v_1, "e", "i")) + if (habr14(v_1, "o", "u")) + habr14(v_1, "\u00F6", "\u00FC") + } + + function r_more_than_one_syllable_word() { + var v_1 = sbp.cursor, + v_2 = 2, + v_3; + while (true) { + v_3 = sbp.cursor; + while (!sbp.in_grouping(g_vowel, 97, 305)) { + if (sbp.cursor >= sbp.limit) { + sbp.cursor = v_3; + if (v_2 > 0) + return false; + sbp.cursor = v_1; + return true; + } + sbp.cursor++; + } + v_2--; + } + } + + function habr15(v_1, n1, c1) { + while (!sbp.eq_s(n1, c1)) { + if (sbp.cursor >= sbp.limit) + return true; + sbp.cursor++; + } + I_strlen = n1; + if (I_strlen != sbp.limit) + return true; + sbp.cursor = v_1; + return false; + } + + function r_is_reserved_word() { + var v_1 = sbp.cursor; + if (habr15(v_1, 2, "ad")) { + sbp.cursor = v_1; + if (habr15(v_1, 5, "soyad")) + return false; + } + return true; + } + + function r_postlude() { + var v_1 = sbp.cursor; + if (r_is_reserved_word()) + return false; + sbp.limit_backward = v_1; + sbp.cursor = sbp.limit; + r_append_U_to_stems_ending_with_d_or_g(); + sbp.cursor = sbp.limit; + r_post_process_last_consonants(); + return true; + } + this.stem = function() { + if (r_more_than_one_syllable_word()) { + sbp.limit_backward = sbp.cursor; + sbp.cursor = sbp.limit; + r_stem_nominal_verb_suffixes(); + sbp.cursor = sbp.limit; + if (B_c_s_n_s) { + r_stem_noun_suffixes(); + sbp.cursor = sbp.limit_backward; + if (r_postlude()) + return true; + } + } + return false; + } + }; + + /* and return a function that stems a word for the current locale */ + return function(token) { + // for lunr version 2 + if (typeof token.update === "function") { + return token.update(function(word) { + st.setCurrent(word); + st.stem(); + return st.getCurrent(); + }) + } else { // for lunr version <= 1 + st.setCurrent(token); + st.stem(); + return st.getCurrent(); + } + } + })(); + + lunr.Pipeline.registerFunction(lunr.tr.stemmer, 'stemmer-tr'); + + lunr.tr.stopWordFilter = lunr.generateStopWordFilter('acaba altmış altı ama ancak arada aslında ayrıca bana bazı belki ben benden beni benim beri beş bile bin bir biri birkaç birkez birçok birşey birşeyi biz bizden bize bizi bizim bu buna bunda bundan bunlar bunları bunların bunu bunun burada böyle böylece da daha dahi de defa değil diye diğer doksan dokuz dolayı dolayısıyla dört edecek eden ederek edilecek ediliyor edilmesi ediyor elli en etmesi etti ettiği ettiğini eğer gibi göre halen hangi hatta hem henüz hep hepsi her herhangi herkesin hiç hiçbir iki ile ilgili ise itibaren itibariyle için işte kadar karşın katrilyon kendi kendilerine kendini kendisi kendisine kendisini kez ki kim kimden kime kimi kimse kırk milyar milyon mu mü mı nasıl ne neden nedenle nerde nerede nereye niye niçin o olan olarak oldu olduklarını olduğu olduğunu olmadı olmadığı olmak olması olmayan olmaz olsa olsun olup olur olursa oluyor on ona ondan onlar onlardan onları onların onu onun otuz oysa pek rağmen sadece sanki sekiz seksen sen senden seni senin siz sizden sizi sizin tarafından trilyon tüm var vardı ve veya ya yani yapacak yapmak yaptı yaptıkları yaptığı yaptığını yapılan yapılması yapıyor yedi yerine yetmiş yine yirmi yoksa yüz zaten çok çünkü öyle üzere üç şey şeyden şeyi şeyler şu şuna şunda şundan şunları şunu şöyle'.split(' ')); + + lunr.Pipeline.registerFunction(lunr.tr.stopWordFilter, 'stopWordFilter-tr'); + }; +})) \ No newline at end of file diff --git a/mkdocs/contrib/search/prebuild-index.js b/mkdocs/contrib/search/prebuild-index.js new file mode 100644 index 0000000000..ae26da4d73 --- /dev/null +++ b/mkdocs/contrib/search/prebuild-index.js @@ -0,0 +1,53 @@ +var lunr = require('./templates/search/lunr'), + stdin = process.stdin, + stdout = process.stdout, + buffer = []; + +stdin.resume(); +stdin.setEncoding('utf8'); + +stdin.on('data', function (data) { + buffer.push(data); +}); + +stdin.on('end', function () { + var data = JSON.parse(buffer.join('')), + lang = ['en']; + + if (data.config) { + if (data.config.lang && data.config.lang.length) { + lang = data.config.lang; + if (lang.length > 1 || lang[0] !== "en") { + require('./lunr-language/lunr.stemmer.support')(lunr); + if (lang.length > 1) { + require('./lunr-language/lunr.multi')(lunr); + } + for (var i=0; i < lang.length; i++) { + if (lang[i] != 'en') { + require('./lunr-language/lunr.' + lang[i])(lunr); + } + } + } + } + if (data.config.separator && data.config.separator.length) { + lunr.tokenizer.separator = new RegExp(data.config.separator); + } + } + + var idx = lunr(function () { + if (lang.length === 1 && lang[0] !== "en" && lunr[lang[0]]) { + this.use(lunr[lang[0]]); + } else if (lang.length > 1) { + this.use(lunr.multiLanguage.apply(null, lang)); + } + this.field('title'); + this.field('text'); + this.ref('location'); + + data.docs.forEach(function (doc) { + this.add(doc); + }, this); + }); + + stdout.write(JSON.stringify(idx)); +}); diff --git a/mkdocs/contrib/legacy_search/search_index.py b/mkdocs/contrib/search/search_index.py similarity index 79% rename from mkdocs/contrib/legacy_search/search_index.py rename to mkdocs/contrib/search/search_index.py index ba26f02483..2b5587abc7 100644 --- a/mkdocs/contrib/legacy_search/search_index.py +++ b/mkdocs/contrib/search/search_index.py @@ -2,7 +2,11 @@ from __future__ import unicode_literals +import os +import re import json +import logging +import subprocess from mkdocs import utils try: # pragma: no cover @@ -10,6 +14,8 @@ except ImportError: # pragma: no cover from HTMLParser import HTMLParser # noqa +log = logging.getLogger(__name__) + class SearchIndex(object): """ @@ -17,8 +23,9 @@ class SearchIndex(object): tags and their following content are sections). """ - def __init__(self): + def __init__(self, **config): self._entries = [] + self.config = config def _find_toc_by_id(self, toc, id_): """ @@ -37,9 +44,12 @@ def _add_entry(self, title, text, loc): A simple wrapper to add an entry and ensure the contents is UTF8 encoded. """ + text = text.replace('\u00a0', ' ') + text = re.sub(r'[ \t\n\r\f\v]+', ' ', text.strip()) + self._entries.append({ 'title': title, - 'text': utils.text_type(text.strip().encode('utf-8'), encoding='utf-8'), + 'text': utils.text_type(text.encode('utf-8'), encoding='utf-8'), 'location': loc }) @@ -91,8 +101,31 @@ def generate_search_index(self): """python to json conversion""" page_dicts = { 'docs': self._entries, + 'config': self.config } - return json.dumps(page_dicts, sort_keys=True, indent=4) + data = json.dumps(page_dicts, sort_keys=True, separators=(',', ':')) + + if self.config['prebuild_index']: + try: + script_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'prebuild-index.js') + p = subprocess.Popen( + ['node', script_path], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + idx, err = p.communicate(data.encode('utf-8')) + if not err: + idx = idx.decode('utf-8') if hasattr(idx, 'decode') else idx + page_dicts['index'] = json.loads(idx) + data = json.dumps(page_dicts, sort_keys=True, separators=(',', ':')) + log.debug('Pre-built search index created successfully.') + else: + log.warning('Failed to pre-build search index. Error: {}'.format(err)) + except (OSError, IOError, ValueError) as e: + log.warning('Failed to pre-build search index. Error: {}'.format(e)) + + return data def strip_tags(self, html): """strip html tags from data""" diff --git a/mkdocs/contrib/search/templates/search/lunr.js b/mkdocs/contrib/search/templates/search/lunr.js new file mode 100644 index 0000000000..c218cc8979 --- /dev/null +++ b/mkdocs/contrib/search/templates/search/lunr.js @@ -0,0 +1,2986 @@ +/** + * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.1.6 + * 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.1.6" +/*! + * lunr.utils + * Copyright (C) 2018 Oliver Nightingale + */ + +/** + * A namespace containing utils for the rest of the lunr library + */ +lunr.utils = {} + +/** + * Print a warning message to the console. + * + * @param {String} message The message to be printed. + * @memberOf Utils + */ +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 Utils + */ +lunr.utils.asString = function (obj) { + if (obj === void 0 || obj === null) { + return "" + } else { + return obj.toString() + } +} +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 +} +/** + * 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. + * + * @static + * @param {?(string|object|object[])} obj - The object to convert into tokens + * @returns {lunr.Token[]} + */ +lunr.tokenizer = function (obj) { + if (obj == null || obj == undefined) { + return [] + } + + if (Array.isArray(obj)) { + return obj.map(function (t) { + return new lunr.Token(lunr.utils.asString(t).toLowerCase()) + }) + } + + 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) { + tokens.push( + new lunr.Token (str.slice(sliceStart, sliceEnd), { + position: [sliceStart, sliceLength], + index: tokens.length + }) + ) + } + + 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. + * @returns {string[]} + */ +lunr.Pipeline.prototype.runString = function (str) { + var token = new lunr.Token (str) + + 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 cosine 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() * otherVector.magnitude()) +} + +/** + * 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} + */ +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. + * + * @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. + * + * @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, + wildcardFound = false + + /* + * Iterates through all characters within the passed string + * appending a node for each character. + * + * As soon as 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 == "*") { + wildcardFound = true + node.edges[char] = node + node.final = final + + } else { + var next = new lunr.TokenSet + next.final = final + + node.edges[char] = next + node = next + + // TODO: is this needed anymore? + if (wildcardFound) { + node.edges["*"] = root + } + } + } + + 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) { + 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.documentVectors - Document vectors keyed by document reference. + * @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. + * + * 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 + */ + +/** + * 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 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) + + 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 + + if (clause.usePipeline) { + terms = this.pipeline.runString(clause.term) + } 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() + + 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 + + /* + * To support field level boosts a query vector is created per + * field. This vector is populated using the termIndex found for + * the term and a unit value with the appropriate boost applied. + * + * If the query vector for this field does not exist yet it needs + * to be created. + */ + if (queryVectors[field] === undefined) { + queryVectors[field] = new lunr.Vector + } + + /* + * 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, 1 * 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 + } + } + } + } + + var matchingFieldRefs = Object.keys(matchingFields), + results = [], + matches = Object.create(null) + + 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, + 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 = [] + 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 +} + +/** + * 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. + * + * @param {string} field - The name of a field to index in all documents. + */ +lunr.Builder.prototype.field = function (field) { + this._fields.push(field) +} + +/** + * 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. + * + * @param {object} doc - The document to add to the index. + */ +lunr.Builder.prototype.add = function (doc) { + var docRef = doc[this._ref] + + this.documentCount += 1 + + for (var i = 0; i < this._fields.length; i++) { + var fieldName = this._fields[i], + field = doc[fieldName], + tokens = this.tokenizer(field), + 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 < this._fields.length; k++) { + posting[this._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] + } + + for (var i = 0; i < this._fields.length; i++) { + var field = this._fields[i] + accumulator[field] = accumulator[field] / documentsWithField[field] + } + + 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]), + field = fieldRef.fieldName, + fieldLength = this.fieldLengths[fieldRef], + fieldVector = new lunr.Vector, + termFrequencies = this.fieldTermFrequencies[fieldRef], + terms = Object.keys(termFrequencies), + termsLength = terms.length + + 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[field])) + tf) + 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: 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) + 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 + +/** + * 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=0] - Whether the term should have wildcards appended or prepended. + */ + +/** + * 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 + "*" + } + + this.clauses.push(clause) + + return this +} + +/** + * 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. + * + * @param {string} term - The term 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 + * }) + */ +lunr.Query.prototype.term = function (term, options) { + var clause = options || {} + clause.term = term + + 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.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 + } + + 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.parseFieldOrTerm + + 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.parseFieldOrTerm = function (parser) { + var lexeme = parser.peekLexeme() + + if (lexeme == undefined) { + return + } + + switch (lexeme.type) { + 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.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 + 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 + 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 + 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 + })) +})(); diff --git a/mkdocs/contrib/search/templates/search/main.js b/mkdocs/contrib/search/templates/search/main.js new file mode 100644 index 0000000000..eaca799ce7 --- /dev/null +++ b/mkdocs/contrib/search/templates/search/main.js @@ -0,0 +1,94 @@ +function getSearchTermFromLocation() { + var sPageURL = window.location.search.substring(1); + var sURLVariables = sPageURL.split('&'); + for (var i = 0; i < sURLVariables.length; i++) { + var sParameterName = sURLVariables[i].split('='); + if (sParameterName[0] == 'q') { + return decodeURIComponent(sParameterName[1].replace(/\+/g, '%20')); + } + } +} + +function formatResult (location, title, summary) { + return ''; +} + +function displayResults (results) { + var search_results = document.getElementById("mkdocs-search-results"); + while (search_results.firstChild) { + search_results.removeChild(search_results.firstChild); + } + if (results.length > 0){ + for (var i=0; i < results.length; i++){ + var result = results[i]; + var html = formatResult(result.location, result.title, result.summary); + search_results.insertAdjacentHTML('beforeend', html); + } + } else { + search_results.insertAdjacentHTML('beforeend', "

No results found

"); + } +} + +function doSearch () { + var query = document.getElementById('mkdocs-search-query').value; + if (query.length > 2) { + console.log('Searching with query: ' + query); + if (!window.Worker) { + displayResults(search(query)); + } else { + searchWorker.postMessage({query: query}); + } + } else { + // Clear results for short queries + displayResults([]); + } +} + +function initSearch () { + var search_input = document.getElementById('mkdocs-search-query'); + if (search_input) { + search_input.addEventListener("keyup", doSearch); + } + + var term = getSearchTermFromLocation(); + if (term) { + search_input.value = term; + doSearch(); + } +} + +function onWorkerMessage (e) { + if (e.data.results) { + var results = e.data.results; + displayResults(results); + } +} + +if (!window.Worker) { + console.log('Web Worker API not supported'); + // load index in main thread + $.getScript(base_url + "/search/worker.js").done(function () { + console.log('Loaded worker'); + init(); + }).fail(function (jqxhr, settings, exception) { + console.error('Could not load worker.js'); + }); +} else { + // Wrap search in a web worker + var searchWorker = new Worker(base_url + "/search/worker.js"); + searchWorker.postMessage({init: true}); + searchWorker.onmessage = onWorkerMessage; +} + +$(function() { + var search_input = document.getElementById('mkdocs-search-query'); + if (search_input) { + search_input.addEventListener("keyup", doSearch); + } + + var term = getSearchTermFromLocation(); + if (term) { + search_input.value = term; + doSearch(); + } +}); diff --git a/mkdocs/contrib/search/templates/search/worker.js b/mkdocs/contrib/search/templates/search/worker.js new file mode 100644 index 0000000000..5a3692cf29 --- /dev/null +++ b/mkdocs/contrib/search/templates/search/worker.js @@ -0,0 +1,127 @@ +var base_path = 'function' === typeof importScripts ? '.' : '/search/'; +var allowSearch = false; +var index; +var documents = {}; +var lang = ['en']; +var data; + +function getScript(script, callback) { + console.log('Loading script: ' + script); + $.getScript(base_path + script).done(function () { + callback(); + }).fail(function (jqxhr, settings, exception) { + console.log('Error: ' + exception); + }); +} + +function getScriptsInOrder(scripts, callback) { + if (scripts.length === 0) { + callback(); + return; + } + getScript(scripts[0], function() { + getScriptsInOrder(scripts.slice(1), callback); + }); +} + +function loadScripts(urls, callback) { + if( 'function' === typeof importScripts ) { + importScripts.apply(null, urls); + callback(); + } else { + getScriptsInOrder(urls, callback); + } +} + +function onJSONLoaded () { + data = JSON.parse(this.responseText); + var scriptsToLoad = ['lunr.js']; + if (data.config && data.config.lang && data.config.lang.length) { + lang = data.config.lang; + } + if (lang.length > 1 || lang[0] !== "en") { + scriptsToLoad.push('lunr.stemmer.support.js'); + if (lang.length > 1) { + scriptsToLoad.push('lunr.multi.js'); + } + for (var i=0; i < lang.length; i++) { + if (lang[i] != 'en') { + scriptsToLoad.push(['lunr', lang[i], 'js'].join('.')); + } + } + } + loadScripts(scriptsToLoad, onScriptsLoaded); +} + +function onScriptsLoaded () { + console.log('All search scripts loaded, building Lunr index...'); + if (data.config && data.config.separator && data.config.separator.length) { + lunr.tokenizer.separator = new RegExp(data.config.separator); + } + if (data.index) { + index = lunr.Index.load(data.index); + data.docs.forEach(function (doc) { + documents[doc.location] = doc; + }); + console.log('Lunr pre-built index loaded, search ready'); + } else { + index = lunr(function () { + if (lang.length === 1 && lang[0] !== "en" && lunr[lang[0]]) { + this.use(lunr[lang[0]]); + } else if (lang.length > 1) { + this.use(lunr.multiLanguage.apply(null, lang)); // spread operator not supported in all browsers: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator#Browser_compatibility + } + this.field('title'); + this.field('text'); + this.ref('location'); + + for (var i=0; i < data.docs.length; i++) { + var doc = data.docs[i]; + this.add(doc); + documents[doc.location] = doc; + } + }); + console.log('Lunr index built, search ready'); + } + allowSearch = true; +} + +function init () { + var oReq = new XMLHttpRequest(); + oReq.addEventListener("load", onJSONLoaded); + var index_path = base_path + '/search_index.json'; + if( 'function' === typeof importScripts ){ + index_path = 'search_index.json'; + } + oReq.open("GET", index_path); + oReq.send(); +} + +function search (query) { + if (!allowSearch) { + console.error('Assets for search still loading'); + return; + } + + var resultDocuments = []; + var results = index.search(query); + for (var i=0; i < results.length; i++){ + var result = results[i]; + doc = documents[result.ref]; + doc.summary = doc.text.substring(0, 200); + resultDocuments.push(doc); + } + return resultDocuments; +} + +if( 'function' === typeof importScripts ) { + onmessage = function (e) { + if (e.data.init) { + init(); + } else if (e.data.query) { + postMessage({ results: search(e.data.query) }); + } else { + console.error("Worker - Unrecognized message: " + e); + } + }; +} diff --git a/mkdocs/tests/search_tests.py b/mkdocs/tests/search_tests.py index 244dff6d0e..2444b8efed 100644 --- a/mkdocs/tests/search_tests.py +++ b/mkdocs/tests/search_tests.py @@ -3,9 +3,13 @@ from __future__ import unicode_literals import unittest +import mock +import json from mkdocs import nav -from mkdocs.contrib.legacy_search import search_index as search +from mkdocs.contrib import search +from mkdocs.contrib.search import search_index +from mkdocs.config.config_options import ValidationError from mkdocs.tests.base import dedent, markdown_to_toc, load_config @@ -13,11 +17,173 @@ def strip_whitespace(string): return string.replace("\n", "").replace(" ", "") -class SearchTests(unittest.TestCase): +class SearchConfigTests(unittest.TestCase): + + def test_lang_default(self): + option = search.LangOption(default=['en']) + value = option.validate(None) + self.assertEqual(['en'], value) + + def test_lang_str(self): + option = search.LangOption() + value = option.validate('en') + self.assertEqual(['en'], value) + + def test_lang_list(self): + option = search.LangOption() + value = option.validate(['en']) + self.assertEqual(['en'], value) + + def test_lang_multi_list(self): + option = search.LangOption() + value = option.validate(['en', 'es', 'fr']) + self.assertEqual(['en', 'es', 'fr'], value) + + def test_lang_bad_type(self): + option = search.LangOption() + self.assertRaises(ValidationError, option.validate, {}) + + def test_lang_bad_code(self): + option = search.LangOption() + self.assertRaises(ValidationError, option.validate, ['foo']) + + def test_lang_good_and_bad_code(self): + option = search.LangOption() + self.assertRaises(ValidationError, option.validate, ['en', 'foo']) + + +class SearchPluginTests(unittest.TestCase): + + def test_plugin_config_defaults(self): + expected = { + 'lang': ['en'], + 'separator': r'[\s\-]+', + 'prebuild_index': False + } + plugin = search.SearchPlugin() + errors, warnings = plugin.load_config({}) + self.assertEqual(plugin.config, expected) + self.assertEqual(errors, []) + self.assertEqual(warnings, []) + + def test_plugin_config_lang(self): + expected = { + 'lang': ['es'], + 'separator': r'[\s\-]+', + 'prebuild_index': False + } + plugin = search.SearchPlugin() + errors, warnings = plugin.load_config({'lang': 'es'}) + self.assertEqual(plugin.config, expected) + self.assertEqual(errors, []) + self.assertEqual(warnings, []) + + def test_plugin_config_separator(self): + expected = { + 'lang': ['en'], + 'separator': r'[\s\-\.]+', + 'prebuild_index': False + } + plugin = search.SearchPlugin() + errors, warnings = plugin.load_config({'separator': r'[\s\-\.]+'}) + self.assertEqual(plugin.config, expected) + self.assertEqual(errors, []) + self.assertEqual(warnings, []) + + def test_plugin_config_prebuild_index(self): + expected = { + 'lang': ['en'], + 'separator': r'[\s\-]+', + 'prebuild_index': True + } + plugin = search.SearchPlugin() + errors, warnings = plugin.load_config({'prebuild_index': True}) + self.assertEqual(plugin.config, expected) + self.assertEqual(errors, []) + self.assertEqual(warnings, []) + + def test_event_on_config_defaults(self): + plugin = search.SearchPlugin() + plugin.load_config({}) + result = plugin.on_config(load_config(theme='mkdocs', extra_javascript=[])) + self.assertFalse(result['theme']['search_index_only']) + self.assertFalse(result['theme']['include_search_page']) + self.assertEqual(result['theme'].static_templates, set(['404.html', 'sitemap.xml'])) + self.assertEqual(len(result['theme'].dirs), 3) + self.assertEqual(result['extra_javascript'], ['search/main.js']) + + def test_event_on_config_include_search_page(self): + plugin = search.SearchPlugin() + plugin.load_config({}) + config = load_config(theme={'name': 'mkdocs', 'include_search_page': True}, extra_javascript=[]) + result = plugin.on_config(config) + self.assertFalse(result['theme']['search_index_only']) + self.assertTrue(result['theme']['include_search_page']) + self.assertEqual(result['theme'].static_templates, set(['404.html', 'sitemap.xml', 'search.html'])) + self.assertEqual(len(result['theme'].dirs), 3) + self.assertEqual(result['extra_javascript'], ['search/main.js']) + + def test_event_on_config_search_index_only(self): + plugin = search.SearchPlugin() + plugin.load_config({}) + config = load_config(theme={'name': 'mkdocs', 'search_index_only': True}, extra_javascript=[]) + result = plugin.on_config(config) + self.assertTrue(result['theme']['search_index_only']) + self.assertFalse(result['theme']['include_search_page']) + self.assertEqual(result['theme'].static_templates, set(['404.html', 'sitemap.xml'])) + self.assertEqual(len(result['theme'].dirs), 2) + self.assertEqual(len(result['extra_javascript']), 0) + + @mock.patch('mkdocs.utils.write_file', autospec=True) + @mock.patch('mkdocs.utils.copy_file', autospec=True) + def test_event_on_post_build_defaults(self, mock_copy_file, mock_write_file): + plugin = search.SearchPlugin() + plugin.load_config({}) + config = load_config(theme='mkdocs') + plugin.on_pre_build(config) + plugin.on_post_build(config) + self.assertEqual(mock_copy_file.call_count, 0) + self.assertEqual(mock_write_file.call_count, 1) + + @mock.patch('mkdocs.utils.write_file', autospec=True) + @mock.patch('mkdocs.utils.copy_file', autospec=True) + def test_event_on_post_build_single_lang(self, mock_copy_file, mock_write_file): + plugin = search.SearchPlugin() + plugin.load_config({'lang': ['es']}) + config = load_config(theme='mkdocs') + plugin.on_pre_build(config) + plugin.on_post_build(config) + self.assertEqual(mock_copy_file.call_count, 2) + self.assertEqual(mock_write_file.call_count, 1) + + @mock.patch('mkdocs.utils.write_file', autospec=True) + @mock.patch('mkdocs.utils.copy_file', autospec=True) + def test_event_on_post_build_multi_lang(self, mock_copy_file, mock_write_file): + plugin = search.SearchPlugin() + plugin.load_config({'lang': ['es', 'fr']}) + config = load_config(theme='mkdocs') + plugin.on_pre_build(config) + plugin.on_post_build(config) + self.assertEqual(mock_copy_file.call_count, 4) + self.assertEqual(mock_write_file.call_count, 1) + + @mock.patch('mkdocs.utils.write_file', autospec=True) + @mock.patch('mkdocs.utils.copy_file', autospec=True) + def test_event_on_post_build_search_index_only(self, mock_copy_file, mock_write_file): + plugin = search.SearchPlugin() + plugin.load_config({'lang': ['es']}) + config = load_config(theme={'name': 'mkdocs', 'search_index_only': True}) + plugin.on_pre_build(config) + plugin.on_post_build(config) + self.assertEqual(mock_copy_file.call_count, 0) + self.assertEqual(mock_write_file.call_count, 1) + + +class SearchIndexTests(unittest.TestCase): def test_html_stripper(self): - stripper = search.HTMLStripper() + stripper = search_index.HTMLStripper() stripper.feed("

Testing

Content

") @@ -25,12 +191,12 @@ def test_html_stripper(self): def test_content_parser(self): - parser = search.ContentParser() + parser = search_index.ContentParser() parser.feed('

Title

TEST') parser.close() - self.assertEquals(parser.data, [search.ContentSection( + self.assertEquals(parser.data, [search_index.ContentSection( text=["TEST"], id_="title", title="Title" @@ -38,12 +204,12 @@ def test_content_parser(self): def test_content_parser_no_id(self): - parser = search.ContentParser() + parser = search_index.ContentParser() parser.feed("

Title

TEST") parser.close() - self.assertEquals(parser.data, [search.ContentSection( + self.assertEquals(parser.data, [search_index.ContentSection( text=["TEST"], id_=None, title="Title" @@ -51,12 +217,12 @@ def test_content_parser_no_id(self): def test_content_parser_content_before_header(self): - parser = search.ContentParser() + parser = search_index.ContentParser() parser.feed("Content Before H1

Title

TEST") parser.close() - self.assertEquals(parser.data, [search.ContentSection( + self.assertEquals(parser.data, [search_index.ContentSection( text=["TEST"], id_=None, title="Title" @@ -64,7 +230,7 @@ def test_content_parser_content_before_header(self): def test_content_parser_no_sections(self): - parser = search.ContentParser() + parser = search_index.ContentParser() parser.feed("No H1 or H2TitleTEST") @@ -75,7 +241,7 @@ def test_find_toc_by_id(self): Test finding the relevant TOC item by the tag ID. """ - index = search.SearchIndex() + index = search_index.SearchIndex() md = dedent(""" # Heading 1 @@ -129,7 +295,7 @@ def test_create_search_index(self): page.toc = toc page.content = html_content - index = search.SearchIndex() + index = search_index.SearchIndex() index.add_entry_from_context(page) self.assertEqual(len(index._entries), 4) @@ -151,3 +317,94 @@ def test_create_search_index(self): self.assertEqual(index._entries[3]['title'], "Heading 3") self.assertEqual(strip_whitespace(index._entries[3]['text']), "Content3") self.assertEqual(index._entries[3]['location'], "{0}#heading-3".format(loc)) + + @mock.patch('subprocess.Popen', autospec=True) + def test_prebuild_index(self, mock_popen): + # See https://stackoverflow.com/a/36501078/866026 + mock_popen.return_value = mock.Mock() + mock_popen_obj = mock_popen.return_value + mock_popen_obj.communicate.return_value = ('{"mock": "index"}', None) + mock_popen_obj.returncode = 0 + + index = search_index.SearchIndex(prebuild_index=True) + expected = { + 'docs': [], + 'config': {'prebuild_index': True}, + 'index': {'mock': 'index'} + } + result = json.loads(index.generate_search_index()) + self.assertEqual(mock_popen.call_count, 1) + self.assertEqual(mock_popen_obj.communicate.call_count, 1) + self.assertEqual(result, expected) + + @mock.patch('subprocess.Popen', autospec=True) + def test_prebuild_index_returns_error(self, mock_popen): + # See https://stackoverflow.com/a/36501078/866026 + mock_popen.return_value = mock.Mock() + mock_popen_obj = mock_popen.return_value + mock_popen_obj.communicate.return_value = ('', 'Some Error') + mock_popen_obj.returncode = 0 + + index = search_index.SearchIndex(prebuild_index=True) + expected = { + 'docs': [], + 'config': {'prebuild_index': True} + } + result = json.loads(index.generate_search_index()) + self.assertEqual(mock_popen.call_count, 1) + self.assertEqual(mock_popen_obj.communicate.call_count, 1) + self.assertEqual(result, expected) + + @mock.patch('subprocess.Popen', autospec=True) + def test_prebuild_index_raises_ioerror(self, mock_popen): + # See https://stackoverflow.com/a/36501078/866026 + mock_popen.return_value = mock.Mock() + mock_popen_obj = mock_popen.return_value + mock_popen_obj.communicate.side_effect = IOError + mock_popen_obj.returncode = 1 + + index = search_index.SearchIndex(prebuild_index=True) + expected = { + 'docs': [], + 'config': {'prebuild_index': True} + } + result = json.loads(index.generate_search_index()) + self.assertEqual(mock_popen.call_count, 1) + self.assertEqual(mock_popen_obj.communicate.call_count, 1) + self.assertEqual(result, expected) + + @mock.patch('subprocess.Popen', autospec=True, side_effect=OSError) + def test_prebuild_index_raises_oserror(self, mock_popen): + # See https://stackoverflow.com/a/36501078/866026 + mock_popen.return_value = mock.Mock() + mock_popen_obj = mock_popen.return_value + mock_popen_obj.communicate.return_value = ('', '') + mock_popen_obj.returncode = 0 + + index = search_index.SearchIndex(prebuild_index=True) + expected = { + 'docs': [], + 'config': {'prebuild_index': True} + } + result = json.loads(index.generate_search_index()) + self.assertEqual(mock_popen.call_count, 1) + self.assertEqual(mock_popen_obj.communicate.call_count, 0) + self.assertEqual(result, expected) + + @mock.patch('subprocess.Popen', autospec=True) + def test_prebuild_index_false(self, mock_popen): + # See https://stackoverflow.com/a/36501078/866026 + mock_popen.return_value = mock.Mock() + mock_popen_obj = mock_popen.return_value + mock_popen_obj.communicate.return_value = ('', '') + mock_popen_obj.returncode = 0 + + index = search_index.SearchIndex(prebuild_index=False) + expected = { + 'docs': [], + 'config': {'prebuild_index': False} + } + result = json.loads(index.generate_search_index()) + self.assertEqual(mock_popen.call_count, 0) + self.assertEqual(mock_popen_obj.communicate.call_count, 0) + self.assertEqual(result, expected) diff --git a/setup.py b/setup.py index f50951e667..9b4346d9c0 100755 --- a/setup.py +++ b/setup.py @@ -72,7 +72,7 @@ def get_packages(package): 'readthedocs = mkdocs.themes.readthedocs', ], 'mkdocs.plugins': [ - 'search = mkdocs.contrib.legacy_search:SearchPlugin', + 'search = mkdocs.contrib.search:SearchPlugin', ], }, classifiers=[ From fc0a36f752eda3682b160307b70cffa0ecba2254 Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Thu, 8 Mar 2018 21:34:34 -0500 Subject: [PATCH 024/342] Simplify path_2_url. We don't need most of the functionality in pathname2url so we might as well only use the part we need. Fixes #1429. --- mkdocs/tests/utils/utils_tests.py | 2 ++ mkdocs/utils/__init__.py | 9 +-------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/mkdocs/tests/utils/utils_tests.py b/mkdocs/tests/utils/utils_tests.py index c65390705e..9dc33ab295 100644 --- a/mkdocs/tests/utils/utils_tests.py +++ b/mkdocs/tests/utils/utils_tests.py @@ -75,7 +75,9 @@ def test_create_media_urls(self): '//media.cdn.org/jquery.js': '//media.cdn.org/jquery.js', 'media.cdn.org/jquery.js': './media.cdn.org/jquery.js', 'local/file/jquery.js': './local/file/jquery.js', + 'local\\windows\\file\\jquery.js': './local/windows/file/jquery.js', 'image.png': './image.png', + 'style.css?v=20180308c': './style.css?v=20180308c' } site_navigation = nav.SiteNavigation(load_config(pages=pages)) for path, expected_result in expected_results.items(): diff --git a/mkdocs/utils/__init__.py b/mkdocs/utils/__init__.py index 47901f7c5b..2a927e02ae 100644 --- a/mkdocs/utils/__init__.py +++ b/mkdocs/utils/__init__.py @@ -22,11 +22,9 @@ try: # pragma: no cover from urllib.parse import urlparse, urlunparse, urljoin # noqa - from urllib.request import pathname2url # noqa from collections import UserDict # noqa except ImportError: # pragma: no cover from urlparse import urlparse, urlunparse, urljoin # noqa - from urllib import pathname2url # noqa from UserDict import UserDict # noqa @@ -360,12 +358,7 @@ def create_relative_media_url(nav, url): def path_to_url(path): """Convert a system path to a URL.""" - if os.path.sep == '/': - return path - - if sys.version_info < (3, 0): - path = path.encode('utf8') - return pathname2url(path) + return '/'.join(path.split('\\')) def get_theme_dir(name): From 5ee6600c950deaf5ac2d8744f0aebb30e5b9f0af Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Fri, 10 Nov 2017 15:25:19 -0500 Subject: [PATCH 025/342] Link to GitHub issues from release notes. Uses the `mdx_gh_links` Markdown extension. Fixes #644. --- docs/about/release-notes.md | 1 + mkdocs.yml | 3 +++ requirements/project.txt | 1 + tox.ini | 1 + 4 files changed, 6 insertions(+) diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md index d05d32f28c..8d7d3cdb59 100644 --- a/docs/about/release-notes.md +++ b/docs/about/release-notes.md @@ -47,6 +47,7 @@ authors should review how [search and themes] interact. ### Other Changes and Additions to Development Version +* Link to GitHub issues from release notes (#644). * Expand {sha} and {version} in gh-deploy commit message (#1410). * Compress `sitemap.xml` (#1130). * Defer loading JS scripts (#1380). diff --git a/mkdocs.yml b/mkdocs.yml index 66664b8d49..ae1e80ca40 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -28,6 +28,9 @@ markdown_extensions: permalink:  - admonition - def_list + - mdx_gh_links: + user: mkdocs + repo: mkdocs copyright: Copyright © 2014 Tom Christie, Maintained by the MkDocs Team. google_analytics: ['UA-27795084-5', 'mkdocs.org'] diff --git a/requirements/project.txt b/requirements/project.txt index bc2ab3c5c6..49b494367d 100644 --- a/requirements/project.txt +++ b/requirements/project.txt @@ -4,3 +4,4 @@ livereload>=2.5.1 Markdown>=2.5 PyYAML>=3.10 tornado>=4.1 +mdx_gh_links>=0.2 diff --git a/tox.ini b/tox.ini index 3f0dea3229..131de3db00 100644 --- a/tox.ini +++ b/tox.ini @@ -28,6 +28,7 @@ commands=mdl --style mdl_ruleset.rb README.md CONTRIBUTING.md docs/ basepython = python2.7 passenv=* deps= + mdx_gh_links requests<=2.9.0 LinkChecker commands= From 3f79e60fbda77b9e2e623c6e6791da6181a2c284 Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Fri, 9 Mar 2018 15:30:34 -0500 Subject: [PATCH 026/342] More complete .gitignore --- .gitignore | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 3ba0914fa8..6a9c8a2d8a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,69 @@ -site/ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python env/ +build/ +develop-eggs/ dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports htmlcov/ .tox/ -mkdocs.egg-info/ -*.pyc .coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Scrapy stuff: +.scrapy + +# PyBuilder +target/ + +# IPython Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# virtualenv +venv/ +ENV/ + +# MkDocs documentation +site/ From 1c80f6d6637cc4e471bc3f96c056e1d30e02b2bc Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Fri, 9 Mar 2018 16:10:29 -0500 Subject: [PATCH 027/342] Better document plugin config Fixes #1391. --- docs/user-guide/plugins.md | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/docs/user-guide/plugins.md b/docs/user-guide/plugins.md index 5860096cd7..fbf53d3c8d 100644 --- a/docs/user-guide/plugins.md +++ b/docs/user-guide/plugins.md @@ -67,19 +67,46 @@ All `BasePlugin` subclasses contain the following attributes: #### config_scheme -: A tuple of configuration validation class instances (to be defined in a subclass). +: A tuple of configuration validation instances. Each item must consist of a + two item tuple in which the first item is the string name of the + configuration option and the second item is an instance of + `mkdocs.config.config_options.BaseConfigOption` or any of its subclasses. + + For example, the following `config_scheme` defines three configuration options: `foo`, which accepts a string; `bar`, which accepts an integer; and `baz`, which accepts a boolean value. + + class MyPlugin(mkdocs.plugins.BasePlugin): + config_scheme = ( + ('foo', mkdocs.config.config_options.Type(mkdocs.utils.string_types, default='a default value')), + ('bar', mkdocs.config.config_options.Type(int, default=0)), + ('baz', mkdocs.config.config_options.Type(bool, default=True)) + ) + + When the user's configuration is loaded, the above scheme will be used to + validate the configuration and fill in any defaults for settings not + provided by the user. The validation classes may be any of the classes + provided in `mkdocs.config.config_options` or a third party subclass defined + in the plugin. + + Any settings provided by the user which fail validation or are not defined + in the `config_scheme` will raise a `mkdocs.config.base.ValidationError`. #### config -: A dictionary of configuration options for the plugin which is populated by the - `load_config` method. +: A dictionary of configuration options for the plugin, which is populated by + the `load_config` method after configuration validation has completed. Use + this attribute to access options provided by the user. + + def on_pre_build(self, config): + if self.config['bool_option']: + # implement "bool_option" functionality here... All `BasePlugin` subclasses contain the following method(s): #### load_config(options) -: Loads configuration from a dictionary of options. Returns a tuple of `(errors, - warnings)`. +: Loads configuration from a dictionary of options. Returns a tuple of + `(errors, warnings)`. This method is called by MkDocs during configuration + validation and should not need to be called by the plugin. #### on_<event_name>() From 71ebf353e6fe8827d8fb2d6c74a077f336782eca Mon Sep 17 00:00:00 2001 From: Felix Eckhofer Date: Thu, 15 Mar 2018 14:31:51 +0100 Subject: [PATCH 028/342] Add support for GitLab repositories (#1435) Note that the icons in the themes will not show up until FontAwesome is updated to at least version 4.6 and/or an up-to-date version of the upstream readthedocs css is imported. This commit also fixes a slight documentation error regarding the default value of `repo_name` and removes some tautological tests. --- docs/user-guide/configuration.md | 36 +++++++++++---------- mkdocs/config/config_options.py | 4 ++- mkdocs/config/defaults.py | 3 +- mkdocs/tests/config/config_options_tests.py | 17 ++++++++-- mkdocs/themes/mkdocs/nav.html | 4 +++ mkdocs/themes/readthedocs/breadcrumbs.html | 2 ++ mkdocs/themes/readthedocs/versions.html | 2 ++ 7 files changed, 46 insertions(+), 22 deletions(-) diff --git a/docs/user-guide/configuration.md b/docs/user-guide/configuration.md index 7ed48b26db..333b7e678c 100644 --- a/docs/user-guide/configuration.md +++ b/docs/user-guide/configuration.md @@ -35,7 +35,8 @@ URL to the generated HTML header. ### repo_url -When set, provides a link to your GitHub or Bitbucket repository on each page. +When set, provides a link to your repository (GitHub, Bitbucket, GitLab, ...) +on each page. ```yaml repo_url: https://github.com/example/repository/ @@ -45,10 +46,10 @@ repo_url: https://github.com/example/repository/ ### repo_name -When set, provides a link to your GitHub or Bitbucket repository on each page. +When set, provides the name for the link to your repository on each page. -**default**: `'GitHub'` or `'Bitbucket'` if the `repo_url` matches those -domains, otherwise `null` +**default**: `'GitHub'`, `'Bitbucket'` or `'GitLab'` if the `repo_url` matches +those domains, otherwise the hostname from the `repo_url`. ### edit_uri @@ -86,14 +87,14 @@ edit_uri: root/path/docs/ ``` !!! note - On a few known hosts (specifically GitHub and Bitbucket), the `edit_uri` is - derived from the 'repo_url' and does not need to be set manually. Simply - defining a `repo_url` will automatically populate the `edit_uri` config - setting. + On a few known hosts (specifically GitHub, Bitbucket and GitLab), the + `edit_uri` is derived from the 'repo_url' and does not need to be set + manually. Simply defining a `repo_url` will automatically populate the + `edit_uri` configs setting. - For example, for a GitHub-hosted repository, the `edit_uri` would be - automatically set as `edit/master/docs/` (Note the `edit` path and `master` - branch). + For example, for a GitHub- or GitLab-hosted repository, the `edit_uri` + would be automatically set as `edit/master/docs/` (Note the `edit` path + and `master` branch). For a Bitbucket-hosted repository, the equivalent `edit_uri` would be automatically set as `src/default/docs/` (note the `src` path and `default` @@ -105,15 +106,16 @@ edit_uri: root/path/docs/ string to disable the automatic setting. !!! warning - On GitHub, the default "edit" path (`edit/master/docs/`) opens the page in - the online GitHub editor. This functionality requires that the user have and - be logged in to a GitHub account. Otherwise, the user will be redirected to - a login/signup page. Alternatively, use the "blob" path + On GitHub and GitLab, the default "edit" path (`edit/master/docs/`) opens + the page in the online editor. This functionality requires that the user + have and be logged in to a GitHub/GitLab account. Otherwise, the user will + be redirected to a login/signup page. Alternatively, use the "blob" path (`blob/master/docs/`) to open a read-only view, which supports anonymous access. -**default**: `edit/master/docs/` or `src/default/docs/` for GitHub or Bitbucket -repos, respectively, if `repo_url` matches those domains, otherwise `null` +**default**: `edit/master/docs/` for GitHub and GitLab repos or +`src/default/docs/` for a Bitbucket repo, if `repo_url` matches those domains, +otherwise `null` ### site_description diff --git a/mkdocs/config/config_options.py b/mkdocs/config/config_options.py index 87c09a82d3..9a7eb0b6c2 100644 --- a/mkdocs/config/config_options.py +++ b/mkdocs/config/config_options.py @@ -260,12 +260,14 @@ def post_validation(self, config, key_name): config['repo_name'] = 'GitHub' elif repo_host == 'bitbucket.org': config['repo_name'] = 'Bitbucket' + elif repo_host == 'gitlab.com': + config['repo_name'] = 'GitLab' else: config['repo_name'] = repo_host.split('.')[0].title() # derive edit_uri from repo_name if unset if config['repo_url'] is not None and edit_uri is None: - if repo_host == 'github.com': + if repo_host == 'github.com' or repo_host == 'gitlab.com': edit_uri = 'edit/master/docs/' elif repo_host == 'bitbucket.org': edit_uri = 'src/default/docs/' diff --git a/mkdocs/config/defaults.py b/mkdocs/config/defaults.py index 7a82a5b7cd..2e93695893 100644 --- a/mkdocs/config/defaults.py +++ b/mkdocs/config/defaults.py @@ -68,7 +68,8 @@ # A name to use for the link to the project source repo. # Default, If repo_url is unset then None, otherwise - # "GitHub" or "Bitbucket" for known url or Hostname for unknown urls. + # "GitHub", "Bitbucket" or "GitLab" for known url or Hostname + # for unknown urls. ('repo_name', config_options.Type(utils.string_types)), # Specify a URI to the docs dir in the project source repo, relative to the diff --git a/mkdocs/tests/config/config_options_tests.py b/mkdocs/tests/config/config_options_tests.py index 87394fe53a..17c4141b3c 100644 --- a/mkdocs/tests/config/config_options_tests.py +++ b/mkdocs/tests/config/config_options_tests.py @@ -172,7 +172,6 @@ def test_repo_name_github(self): option = config_options.RepoURL() config = {'repo_url': "https://github.com/mkdocs/mkdocs"} option.post_validation(config, 'repo_url') - self.assertEqual(config['repo_url'], config['repo_url']) self.assertEqual(config['repo_name'], "GitHub") def test_repo_name_bitbucket(self): @@ -180,15 +179,20 @@ def test_repo_name_bitbucket(self): option = config_options.RepoURL() config = {'repo_url': "https://bitbucket.org/gutworth/six/"} option.post_validation(config, 'repo_url') - self.assertEqual(config['repo_url'], config['repo_url']) self.assertEqual(config['repo_name'], "Bitbucket") + def test_repo_name_gitlab(self): + + option = config_options.RepoURL() + config = {'repo_url': "https://gitlab.com/gitlab-org/gitlab-ce/"} + option.post_validation(config, 'repo_url') + self.assertEqual(config['repo_name'], "GitLab") + def test_repo_name_custom(self): option = config_options.RepoURL() config = {'repo_url': "https://launchpad.net/python-tuskarclient"} option.post_validation(config, 'repo_url') - self.assertEqual(config['repo_url'], config['repo_url']) self.assertEqual(config['repo_name'], "Launchpad") def test_edit_uri_github(self): @@ -205,6 +209,13 @@ def test_edit_uri_bitbucket(self): option.post_validation(config, 'repo_url') self.assertEqual(config['edit_uri'], 'src/default/docs/') + def test_edit_uri_gitlab(self): + + option = config_options.RepoURL() + config = {'repo_url': "https://gitlab.com/gitlab-org/gitlab-ce/"} + option.post_validation(config, 'repo_url') + self.assertEqual(config['edit_uri'], 'edit/master/docs/') + def test_edit_uri_custom(self): option = config_options.RepoURL() diff --git a/mkdocs/themes/mkdocs/nav.html b/mkdocs/themes/mkdocs/nav.html index 686cf00fd2..a2cbef28b1 100644 --- a/mkdocs/themes/mkdocs/nav.html +++ b/mkdocs/themes/mkdocs/nav.html @@ -78,6 +78,8 @@ Edit on {{ config.repo_name }} {%- elif config.repo_name == 'Bitbucket' -%} Edit on {{ config.repo_name }} + {%- elif config.repo_name == 'GitLab' -%} + Edit on {{ config.repo_name }} {%- else -%} Edit on {{ config.repo_name }} {%- endif -%} @@ -90,6 +92,8 @@ {{ config.repo_name }} {%- elif config.repo_name == 'Bitbucket' -%} {{ config.repo_name }} + {%- elif config.repo_name == 'GitLab' -%} + {{ config.repo_name }} {%- else -%} {{ config.repo_name }} {%- endif -%} diff --git a/mkdocs/themes/readthedocs/breadcrumbs.html b/mkdocs/themes/readthedocs/breadcrumbs.html index b7311ff146..efc2ab95c0 100644 --- a/mkdocs/themes/readthedocs/breadcrumbs.html +++ b/mkdocs/themes/readthedocs/breadcrumbs.html @@ -19,6 +19,8 @@ class="icon icon-github" {%- elif config.repo_name|lower == 'bitbucket' %} class="icon icon-bitbucket" + {%- elif config.repo_name|lower == 'gitlab' %} + class="icon icon-gitlab" {% endif %}> Edit on {{ config.repo_name }} {% endif %} {%- endblock %} diff --git a/mkdocs/themes/readthedocs/versions.html b/mkdocs/themes/readthedocs/versions.html index 8a0ecef74b..cbf23215be 100644 --- a/mkdocs/themes/readthedocs/versions.html +++ b/mkdocs/themes/readthedocs/versions.html @@ -4,6 +4,8 @@ GitHub {% elif config.repo_name == 'Bitbucket' %} BitBucket + {% elif config.repo_name == 'GitLab' %} + GitLab {% endif %} {% if page.previous_page %} « Previous From 35f6412b51d2a660d185584ee090e5733b27de3a Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Fri, 16 Mar 2018 15:34:42 -0400 Subject: [PATCH 029/342] Update release-notes Whoops, forgot to add this in 71ebf35. --- docs/about/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md index 8d7d3cdb59..5b67206b9c 100644 --- a/docs/about/release-notes.md +++ b/docs/about/release-notes.md @@ -47,6 +47,7 @@ authors should review how [search and themes] interact. ### Other Changes and Additions to Development Version +* Add support for GitLab edit links (#1435). * Link to GitHub issues from release notes (#644). * Expand {sha} and {version} in gh-deploy commit message (#1410). * Compress `sitemap.xml` (#1130). From f5fd2fa1bb08995f6f54d2bf9692d51b3403da6a Mon Sep 17 00:00:00 2001 From: Christian Oliff Date: Fri, 30 Mar 2018 02:06:35 +0900 Subject: [PATCH 030/342] Add .editorconfig (#1451) --- .editorconfig | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..8c0ef430ab --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# editorconfig.org + +root = true + +[*] +charset = utf-8 +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.js] +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false From fa4c7e5bb38b0b046b478cdda9dd3518ab434305 Mon Sep 17 00:00:00 2001 From: James Rhea Date: Thu, 15 Feb 2018 16:19:20 -0500 Subject: [PATCH 031/342] Noted support for python 3.6 "Officially added support for Python 3.6 (#1296)" as of version 0.17.0. Source: http://www.mkdocs.org/about/release-notes/#version-0170-2017-10-19 --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index f92f1820ea..3c70c5d4ab 100644 --- a/docs/index.md +++ b/docs/index.md @@ -68,7 +68,7 @@ $ pip --version pip 1.5.2 ``` -MkDocs supports Python versions 2.7, 3.3, 3.4, 3.5 and pypy. +MkDocs supports Python versions 2.7, 3.3, 3.4, 3.5, 3.6 and pypy. #### Installing Python From d6449f90808b50979694a3e91b49b532f3bf785d Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Tue, 3 Apr 2018 13:47:16 -0400 Subject: [PATCH 032/342] Drop official support for Python 3.3. Also up tornado version to >=5.0. Fixes #1427. --- .travis.yml | 6 ------ appveyor.yml | 3 --- docs/about/release-notes.md | 5 +++++ docs/index.md | 2 +- requirements/project.txt | 2 +- setup.py | 4 ++-- tox.ini | 12 ++++++------ 7 files changed, 15 insertions(+), 19 deletions(-) diff --git a/.travis.yml b/.travis.yml index 44b3122550..c82fab558c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,12 +20,6 @@ matrix: env: TOXENV=py27-min-req - python: '2.7' env: TOXENV=py27-unittests - - python: '3.3' - env: TOXENV=py33-integration - - python: '3.3' - env: TOXENV=py33-min-req - - python: '3.3' - env: TOXENV=py33-unittests - python: '3.4' env: TOXENV=py34-integration - python: '3.4' diff --git a/appveyor.yml b/appveyor.yml index 249cbd25f5..515ad6fc8e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,9 +4,6 @@ environment: - TOXENV: py27-integration - TOXENV: py27-min-req - TOXENV: py27-unittests - - TOXENV: py33-integration - - TOXENV: py33-min-req - - TOXENV: py33-unittests - TOXENV: py34-integration - TOXENV: py34-min-req - TOXENV: py34-unittests diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md index 5b67206b9c..e5a2abaf98 100644 --- a/docs/about/release-notes.md +++ b/docs/about/release-notes.md @@ -47,6 +47,7 @@ authors should review how [search and themes] interact. ### Other Changes and Additions to Development Version +* Drop official support for Python 3.3 and set `tornado>=5.0` (#1427). * Add support for GitLab edit links (#1435). * Link to GitHub issues from release notes (#644). * Expand {sha} and {version} in gh-deploy commit message (#1410). @@ -66,6 +67,10 @@ authors should review how [search and themes] interact. * Update links to Python-Markdown library (#1360). * Document how to generate manpages for MkDocs commands (#686). +## Version 0.17.3 (2018-03-07) + +* Bugfix: Set dependency `tornado>=4.1,<5.0` due to changes in 5.0 (#1428). + ## Version 0.17.2 (2017-11-15) * Bugfix: Correct `extra_*` config setting regressions (#1335 & #1336). diff --git a/docs/index.md b/docs/index.md index 3c70c5d4ab..e832d40bf4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -68,7 +68,7 @@ $ pip --version pip 1.5.2 ``` -MkDocs supports Python versions 2.7, 3.3, 3.4, 3.5, 3.6 and pypy. +MkDocs supports Python versions 2.7, 3.4, 3.5, 3.6 and pypy. #### Installing Python diff --git a/requirements/project.txt b/requirements/project.txt index 49b494367d..418d72c123 100644 --- a/requirements/project.txt +++ b/requirements/project.txt @@ -3,5 +3,5 @@ Jinja2>=2.7.1 livereload>=2.5.1 Markdown>=2.5 PyYAML>=3.10 -tornado>=4.1 +tornado>=5.0 mdx_gh_links>=0.2 diff --git a/setup.py b/setup.py index 9b4346d9c0..0a52f05df3 100755 --- a/setup.py +++ b/setup.py @@ -61,8 +61,9 @@ def get_packages(package): 'livereload>=2.5.1', 'Markdown>=2.3.1', 'PyYAML>=3.10', - 'tornado>=4.1', + 'tornado>=5.0', ], + python_requires='>=2.7.9,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*', entry_points={ 'console_scripts': [ 'mkdocs = mkdocs.__main__:cli', @@ -86,7 +87,6 @@ def get_packages(package): 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', diff --git a/tox.ini b/tox.ini index 131de3db00..52d0d7e0e1 100644 --- a/tox.ini +++ b/tox.ini @@ -1,18 +1,18 @@ [tox] envlist = - py{27,33,34,35,36}-{unittests,integration,min-req}, + py{27,34,35,36}-{unittests,integration,min-req}, flake8, markdown-lint, linkchecker, jshint, csslint [testenv] passenv = LANG deps= - py{27,33,34,35,36,py,py3}-{unittests,integration}: -rrequirements/project.txt - py{27,33,34,35,36,py,py3}-min-req: -rrequirements/project-min.txt - py{27,33,34,35,36,py,py3}-{unittests,min-req}: -rrequirements/test.txt + py{27,34,35,36,py,py3}-{unittests,integration}: -rrequirements/project.txt + py{27,34,35,36,py,py3}-min-req: -rrequirements/project-min.txt + py{27,34,35,36,py,py3}-{unittests,min-req}: -rrequirements/test.txt commands= {envpython} --version - py{27,33,34,35,36,py,py3}-{unittests,min-req}: {envbindir}/nosetests --with-coverage --cover-package mkdocs mkdocs - py{27,33,34,35,36,py,py3}-integration: {envpython} -m mkdocs.tests.integration --output={envtmpdir}/builds + py{27,34,35,36,py,py3}-{unittests,min-req}: {envbindir}/nosetests --with-coverage --cover-package mkdocs mkdocs + py{27,34,35,36,py,py3}-integration: {envpython} -m mkdocs.tests.integration --output={envtmpdir}/builds [testenv:flake8] basepython = python2.7 From a3e60f138508485f63cab20047de0ba72f32b7e6 Mon Sep 17 00:00:00 2001 From: Joseph Lombrozo Date: Wed, 4 Apr 2018 09:58:38 -0600 Subject: [PATCH 033/342] Paths in config file are relative to the config file. (#1376) --- docs/about/release-notes.md | 19 +++ docs/user-guide/configuration.md | 23 +-- docs/user-guide/custom-themes.md | 57 ++++--- docs/user-guide/styling-your-docs.md | 2 +- mkdocs/config/base.py | 5 +- mkdocs/config/config_options.py | 45 ++++- mkdocs/plugins.py | 4 +- mkdocs/tests/build_tests.py | 29 +--- mkdocs/tests/config/base_tests.py | 51 +++++- mkdocs/tests/config/config_options_tests.py | 31 +++- mkdocs/tests/config/config_tests.py | 174 ++++++++++---------- mkdocs/tests/plugin_tests.py | 28 +++- tox.ini | 3 +- 13 files changed, 294 insertions(+), 177 deletions(-) diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md index e5a2abaf98..9ae74fb152 100644 --- a/docs/about/release-notes.md +++ b/docs/about/release-notes.md @@ -25,6 +25,25 @@ The current and past members of the MkDocs team. ### Major Additions to Development Version +#### Path Based Settings are Relative to Configuration File (#543) + +Previously any relative paths in the various configuration options were +resolved relative to the current working directory. They are now resolved +relative to the configuration file. As the documentation has always encouraged +running the various MkDocs commands from the directory that contains the +configuration file (project root), this change will not affect most users. +However, it will make it much easier to implement automated builds or otherwise +run commands from a location other than the project root. + +Simply use the `-f/--config-file` option and point it at the configuration file: + +```sh +mkdocs build --config-file /path/to/my/config/file.yml +``` + +As previously, if no file is specified, MkDocs looks for a file named +`mkdocs.yml` in the current working directory. + #### Refactor Search Plugin The search plugin has been completely refactored to include support for the diff --git a/docs/user-guide/configuration.md b/docs/user-guide/configuration.md index 333b7e678c..8cac62f917 100644 --- a/docs/user-guide/configuration.md +++ b/docs/user-guide/configuration.md @@ -215,9 +215,10 @@ If a set of key/value pairs, the following nested keys can be defined: #### custom_dir: - A directory to custom a theme. This can either be a relative directory, in - which case it is resolved relative to the directory containing your - configuration file, or it can be an absolute directory path. + A directory containing a custom theme. This can either be a relative + directory, in which case it is resolved relative to the directory containing + your configuration file, or it can be an absolute directory path from the + root of your local file system. See [styling your docs][theme_dir] for details if you would like to tweak an existing theme. @@ -240,19 +241,19 @@ If a set of key/value pairs, the following nested keys can be defined: ### docs_dir -Lets you set the directory containing the documentation source markdown files. -This can either be a relative directory, in which case it is resolved relative -to the directory containing your configuration file, or it can be an absolute -directory path from the root of your local file system. +The directory containing the documentation source markdown files. This can +either be a relative directory, in which case it is resolved relative to the +directory containing your configuration file, or it can be an absolute directory +path from the root of your local file system. **default**: `'docs'` ### site_dir -Lets you set the directory where the output HTML and other files are created. -This can either be a relative directory, in which case it is resolved relative -to the directory containing your configuration file, or it can be an absolute -directory path from the root of your local file system. +The directory where the output HTML and other files are created. This can either +be a relative directory, in which case it is resolved relative to the directory +containing your configuration file, or it can be an absolute directory path from +the root of your local file system. **default**: `'site'` diff --git a/docs/user-guide/custom-themes.md b/docs/user-guide/custom-themes.md index 2f0021dd89..18f72b282a 100644 --- a/docs/user-guide/custom-themes.md +++ b/docs/user-guide/custom-themes.md @@ -20,41 +20,48 @@ and their usage. ## Creating a custom theme The bare minimum required for a custom theme is a `main.html` [Jinja2 template] -file. This should be placed in a directory which will be the `custom_dir` and it -should be created next to the `mkdocs.yml` configuration file. Within -`mkdocs.yml`, specify the theme `custom_dir` option and set it to the name of -the directory containing `main.html`. For example, given this example project -layout: - - mkdocs.yml - docs/ - index.md - about.md - custom_theme/ - main.html - ... - -You would include the following settings in `mkdocs.yml` to use the custom theme +file which is placed in a directory that is *not* a child of the [docs_dir]. +Within `mkdocs.yml`, set the theme.[custom_dir] option to the path of the +directory containing `main.html`. The path should be relative to the +configuration file. For example, given this example project layout: + +```no-highlight +mkdocs.yml +docs/ + index.md + about.md +custom_theme/ + main.html + ... +``` + +... you would include the following settings in `mkdocs.yml` to use the custom theme directory: - theme: - name: null - custom_dir: 'custom_theme' +```yaml +theme: + name: null + custom_dir: 'custom_theme/' +``` !!! Note - Generally, when building your own custom theme, the theme `name` - configuration setting would be set to `null`. However, if used in - combination with the `custom_dir` configuration value a custom theme can be - used to replace only specific parts of a built-in theme. For example, with - the above layout and if you set `name: "mkdocs"` then the `main.html` file - in the `custom_dir` would replace that in the theme but otherwise the - `mkdocs` theme would remain the same. This is useful if you want to make + Generally, when building your own custom theme, the theme.[name] + configuration setting would be set to `null`. However, if the + theme.[custom_dir] configuration value is used in combination with an + existing theme, the theme.[custom_dir] can be used to replace only specific + parts of a built-in theme. For example, with the above layout and if you set + `name: "mkdocs"` then the `main.html` file in the theme.[custom_dir] would + replace the file of the same name in the `mkdocs` theme but otherwise the + `mkdocs` theme would remain unchanged. This is useful if you want to make small adjustments to an existing theme. For more specific information, see [styling your docs]. [styling your docs]: ./styling-your-docs.md#using-the-theme-custom_dir +[custom_dir]: ./configuration.md#custom_dir +[name]: ./configuration.md#name +[docs_dir]:./configuration.md#docs_dir ## Basic theme diff --git a/docs/user-guide/styling-your-docs.md b/docs/user-guide/styling-your-docs.md index 4837e8f6d4..c97abf99d1 100644 --- a/docs/user-guide/styling-your-docs.md +++ b/docs/user-guide/styling-your-docs.md @@ -138,7 +138,7 @@ And then point your `mkdocs.yml` configuration file at the new directory: ```yaml theme: name: mkdocs - custom_dir: custom_theme + custom_dir: custom_theme/ ``` To override the 404 error page ("file not found"), add a new template file named diff --git a/mkdocs/config/base.py b/mkdocs/config/base.py index afcec0a987..60f9b587ab 100644 --- a/mkdocs/config/base.py +++ b/mkdocs/config/base.py @@ -21,13 +21,14 @@ class Config(utils.UserDict): for running validation on the structure and contents. """ - def __init__(self, schema): + def __init__(self, schema, config_file_path=None): """ The schema is a Python dict which maps the config name to a validator. """ self._schema = schema self._schema_keys = set(dict(schema).keys()) + self.config_file_path = config_file_path self.data = {} self.user_configs = [] @@ -172,7 +173,7 @@ def load_config(config_file=None, **kwargs): # Initialise the config with the default schema . from mkdocs import config - cfg = Config(schema=config.DEFAULT_SCHEMA) + cfg = Config(schema=config.DEFAULT_SCHEMA, config_file_path=options['config_file_path']) # First load the config file cfg.load_file(config_file) # Then load the options to overwrite anything in the config. diff --git a/mkdocs/config/config_options.py b/mkdocs/config/config_options.py index 9a7eb0b6c2..30da2a2ae4 100644 --- a/mkdocs/config/config_options.py +++ b/mkdocs/config/config_options.py @@ -293,6 +293,24 @@ def __init__(self, exists=False, **kwargs): super(FilesystemObject, self).__init__(type_=utils.string_types, **kwargs) self.exists = exists + def pre_validation(self, config, key_name): + value = config[key_name] + + if not value: + return + + if os.path.isabs(value): + return + + if config.config_file_path is None: + # Unable to determine absolute path of the config file; fall back + # to trusting the relative path + return + + config_dir = os.path.dirname(config.config_file_path) + value = os.path.join(config_dir, value) + config[key_name] = value + def run_validation(self, value): value = super(FilesystemObject, self).run_validation(value) if self.exists and not self.existence_test(value): @@ -311,9 +329,11 @@ class Dir(FilesystemObject): name = 'directory' def post_validation(self, config, key_name): + if config.config_file_path is None: + return # Validate that the dir is not the parent dir of the config file. - if os.path.dirname(config['config_file_path']) == config[key_name]: + if os.path.dirname(config.config_file_path) == config[key_name]: raise ValidationError( ("The '{0}' should not be the parent directory of the config " "file. Use a child directory instead so that the config file " @@ -430,7 +450,12 @@ def post_validation(self, config, key_name): # Ensure custom_dir is an absolute path if 'custom_dir' in theme_config and not os.path.isabs(theme_config['custom_dir']): - theme_config['custom_dir'] = os.path.abspath(theme_config['custom_dir']) + config_dir = os.path.dirname(config.config_file_path) + theme_config['custom_dir'] = os.path.join(config_dir, theme_config['custom_dir']) + + if 'custom_dir' in theme_config and not os.path.isdir(theme_config['custom_dir']): + raise ValidationError("The path set in {name}.custom_dir ('{path}') does not exist.". + format(path=theme_config['custom_dir'], name=self.name)) config[key_name] = theme.Theme(**theme_config) @@ -621,6 +646,10 @@ class Plugins(OptionallyRequired): def __init__(self, **kwargs): super(Plugins, self).__init__(**kwargs) self.installed_plugins = plugins.get_plugins() + self.config_file_path = None + + def pre_validation(self, config, key_name): + self.config_file_path = config.config_file_path def run_validation(self, value): if not isinstance(value, (list, tuple)): @@ -635,11 +664,15 @@ def run_validation(self, value): if not isinstance(cfg, dict): raise ValidationError('Invalid config options for ' 'the "{0}" plugin.'.format(name)) - plgins[name] = self.load_plugin(name, cfg) - elif isinstance(item, utils.string_types): - plgins[item] = self.load_plugin(item, {}) + item = name else: + cfg = {} + + if not isinstance(item, utils.string_types): raise ValidationError('Invalid Plugins configuration') + + plgins[item] = self.load_plugin(item, cfg) + return plgins def load_plugin(self, name, config): @@ -654,7 +687,7 @@ def load_plugin(self, name, config): plugins.BasePlugin.__name__)) plugin = Plugin() - errors, warnings = plugin.load_config(config) + errors, warnings = plugin.load_config(config, self.config_file_path) self.warnings.extend(warnings) errors_message = '\n'.join( "Plugin value: '{}'. Error: {}".format(x, y) diff --git a/mkdocs/plugins.py b/mkdocs/plugins.py index 69d99ed82f..de4e837da5 100644 --- a/mkdocs/plugins.py +++ b/mkdocs/plugins.py @@ -42,10 +42,10 @@ class BasePlugin(object): config_scheme = () config = {} - def load_config(self, options): + def load_config(self, options, config_file_path=None): """ Load config from a dict of options. Returns a tuple of (errors, warnings).""" - self.config = Config(schema=self.config_scheme) + self.config = Config(schema=self.config_scheme, config_file_path=config_file_path) self.config.load_dict(options) return self.config.validate() diff --git a/mkdocs/tests/build_tests.py b/mkdocs/tests/build_tests.py index ac5f5116ae..32b86206c3 100644 --- a/mkdocs/tests/build_tests.py +++ b/mkdocs/tests/build_tests.py @@ -3,8 +3,6 @@ from __future__ import unicode_literals import os -import shutil -import tempfile import unittest import mock import io @@ -15,6 +13,11 @@ # In Py3 use builtin zip function pass +try: + # py>=3.2 + from tempfile import TemporaryDirectory +except ImportError: + from backports.tempfile import TemporaryDirectory from mkdocs import nav from mkdocs.commands import build @@ -319,9 +322,7 @@ def test_markdown_duplicate_custom_extension(self): self.assertEqual(page.content.strip(), '

foo

') def test_copying_media(self): - docs_dir = tempfile.mkdtemp() - site_dir = tempfile.mkdtemp() - try: + with TemporaryDirectory() as docs_dir, TemporaryDirectory() as site_dir: # Create a non-empty markdown file, image, html file, dot file and dot directory. f = open(os.path.join(docs_dir, 'index.md'), 'w') f.write(dedent(""" @@ -351,14 +352,9 @@ def test_copying_media(self): self.assertTrue(os.path.isfile(os.path.join(site_dir, 'example.html'))) self.assertFalse(os.path.isfile(os.path.join(site_dir, '.hidden'))) self.assertFalse(os.path.isfile(os.path.join(site_dir, '.git/hidden'))) - finally: - shutil.rmtree(docs_dir) - shutil.rmtree(site_dir) def test_copy_theme_files(self): - docs_dir = tempfile.mkdtemp() - site_dir = tempfile.mkdtemp() - try: + with TemporaryDirectory() as docs_dir, TemporaryDirectory() as site_dir: # Create a non-empty markdown file. f = open(os.path.join(docs_dir, 'index.md'), 'w') f.write(dedent(""" @@ -383,9 +379,6 @@ def test_copy_theme_files(self): self.assertFalse(os.path.isfile(os.path.join(site_dir, 'base.html'))) self.assertFalse(os.path.isfile(os.path.join(site_dir, 'content.html'))) self.assertFalse(os.path.isfile(os.path.join(site_dir, 'nav.html'))) - finally: - shutil.rmtree(docs_dir) - shutil.rmtree(site_dir) def test_strict_mode_valid(self): pages = [ @@ -467,9 +460,7 @@ def test_extra_context(self): self.assertEqual(context['config']['extra']['a'], 1) def test_BOM(self): - docs_dir = tempfile.mkdtemp() - site_dir = tempfile.mkdtemp() - try: + with TemporaryDirectory() as docs_dir, TemporaryDirectory() as site_dir: # Create an UTF-8 Encoded file with BOM (as Micorsoft editors do). See #1186. f = io.open(os.path.join(docs_dir, 'index.md'), 'w', encoding='utf-8-sig') f.write('# An UTF-8 encoded file with a BOM') @@ -490,7 +481,3 @@ def test_BOM(self): self.assertTrue( '

An UTF-8 encoded file with a BOM

' in output ) - - finally: - shutil.rmtree(docs_dir) - shutil.rmtree(site_dir) diff --git a/mkdocs/tests/config/base_tests.py b/mkdocs/tests/config/base_tests.py index ce56cd932b..5118279fe7 100644 --- a/mkdocs/tests/config/base_tests.py +++ b/mkdocs/tests/config/base_tests.py @@ -3,6 +3,12 @@ import tempfile import unittest +try: + # py>=3.2 + from tempfile import TemporaryDirectory +except ImportError: + from backports.tempfile import TemporaryDirectory + from mkdocs import exceptions from mkdocs.config import base, defaults from mkdocs.config.config_options import BaseConfigOption @@ -42,7 +48,9 @@ def test_load_from_file(self): Allows users to specify a config other than the default `mkdocs.yml`. """ - config_file = tempfile.NamedTemporaryFile('w', delete=False) + temp_dir = TemporaryDirectory() + config_file = open(os.path.join(temp_dir.name, 'mkdocs.yml'), 'w') + os.mkdir(os.path.join(temp_dir.name, 'docs')) try: config_file.write("site_name: MkDocs Test\n") config_file.flush() @@ -64,7 +72,12 @@ def test_load_from_open_file(self): `load_config` can accept an open file descriptor. """ - config_file = tempfile.NamedTemporaryFile('r+', delete=False) + temp_dir = TemporaryDirectory() + temp_path = temp_dir.name + config_fname = os.path.join(temp_path, 'mkdocs.yml') + + config_file = open(config_fname, 'w+') + os.mkdir(os.path.join(temp_path, 'docs')) try: config_file.write("site_name: MkDocs Test\n") config_file.flush() @@ -75,7 +88,7 @@ def test_load_from_open_file(self): # load_config will always close the file self.assertTrue(config_file.closed) finally: - os.remove(config_file.name) + temp_dir.cleanup() def test_load_from_closed_file(self): """ @@ -83,7 +96,10 @@ def test_load_from_closed_file(self): Ensure `load_config` reloads the closed file. """ - config_file = tempfile.NamedTemporaryFile('w', delete=False) + temp_dir = TemporaryDirectory() + config_file = open(os.path.join(temp_dir.name, 'mkdocs.yml'), 'w') + os.mkdir(os.path.join(temp_dir.name, 'docs')) + try: config_file.write("site_name: MkDocs Test\n") config_file.flush() @@ -93,7 +109,7 @@ def test_load_from_closed_file(self): self.assertTrue(isinstance(cfg, base.Config)) self.assertEqual(cfg['site_name'], 'MkDocs Test') finally: - os.remove(config_file.name) + temp_dir.cleanup() def test_load_from_deleted_file(self): """ @@ -234,3 +250,28 @@ def post_validation(self, config, key_name): ('invalid_option', 'run_validation warning'), ('invalid_option', 'post_validation warning'), ]) + + def test_load_from_file_with_relative_paths(self): + """ + When explicitly setting a config file, paths should be relative to the + config file, not the working directory. + """ + + config_dir = TemporaryDirectory() + config_fname = os.path.join(config_dir.name, 'mkdocs.yml') + docs_dir = os.path.join(config_dir.name, 'src') + os.mkdir(docs_dir) + + config_file = open(config_fname, 'w') + + try: + config_file.write("docs_dir: src\nsite_name: MkDocs Test\n") + config_file.flush() + config_file.close() + + cfg = base.load_config(config_file=config_file) + self.assertTrue(isinstance(cfg, base.Config)) + self.assertEqual(cfg['site_name'], 'MkDocs Test') + self.assertEqual(cfg['docs_dir'], docs_dir) + finally: + config_dir.cleanup() diff --git a/mkdocs/tests/config/config_options_tests.py b/mkdocs/tests/config/config_options_tests.py index 17c4141b3c..56b7a7240e 100644 --- a/mkdocs/tests/config/config_options_tests.py +++ b/mkdocs/tests/config/config_options_tests.py @@ -6,6 +6,7 @@ import mkdocs from mkdocs import utils from mkdocs.config import config_options +from mkdocs.config.base import Config class OptionallyRequiredTest(unittest.TestCase): @@ -272,18 +273,21 @@ def test_incorrect_type_type_error(self): option.validate, []) def test_doc_dir_is_config_dir(self): + cfg = Config( + [('docs_dir', config_options.Dir())], + config_file_path=os.path.join(os.path.abspath('.'), 'mkdocs.yml'), + ) test_config = { - 'config_file_path': os.path.join(os.path.abspath('.'), 'mkdocs.yml'), 'docs_dir': '.' } - docs_dir = config_options.Dir() + cfg.load_dict(test_config) - test_config['docs_dir'] = docs_dir.validate(test_config['docs_dir']) + fails, warns = cfg.validate() - self.assertRaises(config_options.ValidationError, - docs_dir.post_validation, test_config, 'docs_dir') + self.assertEqual(len(fails), 1) + self.assertEqual(len(warns), 0) class SiteDirTest(unittest.TestCase): @@ -293,12 +297,23 @@ def validate_config(self, config): site_dir = config_options.SiteDir() docs_dir = config_options.Dir() - config['config_file_path'] = os.path.join(os.path.abspath('..'), 'mkdocs.yml') + fname = os.path.join(os.path.abspath('..'), 'mkdocs.yml') config['docs_dir'] = docs_dir.validate(config['docs_dir']) config['site_dir'] = site_dir.validate(config['site_dir']) - site_dir.post_validation(config, 'site_dir') - return True # No errors were raised + + schema = [ + ('site_dir', site_dir), + ('docs_dir', docs_dir), + ] + cfg = Config(schema, fname) + cfg.load_dict(config) + failed, warned = cfg.validate() + + if failed: + raise config_options.ValidationError(failed) + + return True def test_doc_dir_in_site_dir(self): diff --git a/mkdocs/tests/config/config_tests.py b/mkdocs/tests/config/config_tests.py index ebfdba48b3..d956bf3b46 100644 --- a/mkdocs/tests/config/config_tests.py +++ b/mkdocs/tests/config/config_tests.py @@ -3,10 +3,16 @@ from __future__ import unicode_literals import os -import shutil import tempfile import unittest +try: + # py>=3.2 + from tempfile import TemporaryDirectory +except ImportError: + from backports.tempfile import TemporaryDirectory + + import mkdocs from mkdocs import config from mkdocs import utils @@ -81,8 +87,11 @@ def test_config_option(self): pages: - 'Introduction': 'index.md' """) - config_file = tempfile.NamedTemporaryFile('w', delete=False) - try: + with TemporaryDirectory() as temp_path: + os.mkdir(os.path.join(temp_path, 'docs')) + config_path = os.path.join(temp_path, 'mkdocs.yml') + config_file = open(config_path, 'w') + config_file.write(ensure_utf(file_contents)) config_file.flush() config_file.close() @@ -90,93 +99,87 @@ def test_config_option(self): result = config.load_config(config_file=config_file.name) self.assertEqual(result['site_name'], expected_result['site_name']) self.assertEqual(result['pages'], expected_result['pages']) - finally: - os.remove(config_file.name) def test_theme(self): - - mytheme = tempfile.mkdtemp() - custom = tempfile.mkdtemp() - - configs = [ - dict(), # default theme - {"theme": "readthedocs"}, # builtin theme - {"theme_dir": mytheme}, # custom only - {"theme": "readthedocs", "theme_dir": custom}, # builtin and custom - {"theme": {'name': 'readthedocs'}}, # builtin as complex - {"theme": {'name': None, 'custom_dir': mytheme}}, # custom only as complex - {"theme": {'name': 'readthedocs', 'custom_dir': custom}}, # builtin and custom as complex - { # user defined variables - 'theme': { - 'name': 'mkdocs', - 'static_templates': ['foo.html'], - 'show_sidebar': False, - 'some_var': 'bar' + with TemporaryDirectory() as mytheme, TemporaryDirectory() as custom: + configs = [ + dict(), # default theme + {"theme": "readthedocs"}, # builtin theme + {"theme_dir": mytheme}, # custom only + {"theme": "readthedocs", "theme_dir": custom}, # builtin and custom + {"theme": {'name': 'readthedocs'}}, # builtin as complex + {"theme": {'name': None, 'custom_dir': mytheme}}, # custom only as complex + {"theme": {'name': 'readthedocs', 'custom_dir': custom}}, # builtin and custom as complex + { # user defined variables + 'theme': { + 'name': 'mkdocs', + 'static_templates': ['foo.html'], + 'show_sidebar': False, + 'some_var': 'bar' + } } - } - ] - - mkdocs_dir = os.path.abspath(os.path.dirname(mkdocs.__file__)) - mkdocs_templates_dir = os.path.join(mkdocs_dir, 'templates') - theme_dir = os.path.abspath(os.path.join(mkdocs_dir, 'themes')) - - results = ( - { - 'dirs': [os.path.join(theme_dir, 'mkdocs'), mkdocs_templates_dir], - 'static_templates': ['404.html', 'sitemap.xml'], - 'vars': {'include_search_page': False, 'search_index_only': False} - }, { - 'dirs': [os.path.join(theme_dir, 'readthedocs'), mkdocs_templates_dir], - 'static_templates': ['404.html', 'sitemap.xml'], - 'vars': {'include_search_page': True, 'search_index_only': False} - }, { - 'dirs': [mytheme, mkdocs_templates_dir], - 'static_templates': ['sitemap.xml'], - 'vars': {} - }, { - 'dirs': [custom, os.path.join(theme_dir, 'readthedocs'), mkdocs_templates_dir], - 'static_templates': ['404.html', 'sitemap.xml'], - 'vars': {'include_search_page': True, 'search_index_only': False} - }, { - 'dirs': [os.path.join(theme_dir, 'readthedocs'), mkdocs_templates_dir], - 'static_templates': ['404.html', 'sitemap.xml'], - 'vars': {'include_search_page': True, 'search_index_only': False} - }, { - 'dirs': [mytheme, mkdocs_templates_dir], - 'static_templates': ['sitemap.xml'], - 'vars': {} - }, { - 'dirs': [custom, os.path.join(theme_dir, 'readthedocs'), mkdocs_templates_dir], - 'static_templates': ['404.html', 'sitemap.xml'], - 'vars': {'include_search_page': True, 'search_index_only': False} - }, { - 'dirs': [os.path.join(theme_dir, 'mkdocs'), mkdocs_templates_dir], - 'static_templates': ['404.html', 'sitemap.xml', 'foo.html'], - 'vars': { - 'show_sidebar': False, - 'some_var': 'bar', - 'include_search_page': False, - 'search_index_only': False + ] + + mkdocs_dir = os.path.abspath(os.path.dirname(mkdocs.__file__)) + mkdocs_templates_dir = os.path.join(mkdocs_dir, 'templates') + theme_dir = os.path.abspath(os.path.join(mkdocs_dir, 'themes')) + + results = ( + { + 'dirs': [os.path.join(theme_dir, 'mkdocs'), mkdocs_templates_dir], + 'static_templates': ['404.html', 'sitemap.xml'], + 'vars': {'include_search_page': False, 'search_index_only': False} + }, { + 'dirs': [os.path.join(theme_dir, 'readthedocs'), mkdocs_templates_dir], + 'static_templates': ['404.html', 'sitemap.xml'], + 'vars': {'include_search_page': True, 'search_index_only': False} + }, { + 'dirs': [mytheme, mkdocs_templates_dir], + 'static_templates': ['sitemap.xml'], + 'vars': {} + }, { + 'dirs': [custom, os.path.join(theme_dir, 'readthedocs'), mkdocs_templates_dir], + 'static_templates': ['404.html', 'sitemap.xml'], + 'vars': {'include_search_page': True, 'search_index_only': False} + }, { + 'dirs': [os.path.join(theme_dir, 'readthedocs'), mkdocs_templates_dir], + 'static_templates': ['404.html', 'sitemap.xml'], + 'vars': {'include_search_page': True, 'search_index_only': False} + }, { + 'dirs': [mytheme, mkdocs_templates_dir], + 'static_templates': ['sitemap.xml'], + 'vars': {} + }, { + 'dirs': [custom, os.path.join(theme_dir, 'readthedocs'), mkdocs_templates_dir], + 'static_templates': ['404.html', 'sitemap.xml'], + 'vars': {'include_search_page': True, 'search_index_only': False} + }, { + 'dirs': [os.path.join(theme_dir, 'mkdocs'), mkdocs_templates_dir], + 'static_templates': ['404.html', 'sitemap.xml', 'foo.html'], + 'vars': { + 'show_sidebar': False, + 'some_var': 'bar', + 'include_search_page': False, + 'search_index_only': False + } } - } - ) + ) - for config_contents, result in zip(configs, results): + for config_contents, result in zip(configs, results): - c = config.Config(schema=( - ('theme', config_options.Theme(default='mkdocs')), - ('theme_dir', config_options.ThemeDir(exists=True)), - )) - c.load_dict(config_contents) - errors, warnings = c.validate() - self.assertEqual(len(errors), 0) - self.assertEqual(c['theme'].dirs, result['dirs']) - self.assertEqual(c['theme'].static_templates, set(result['static_templates'])) - self.assertEqual(dict([(k, c['theme'][k]) for k in iter(c['theme'])]), result['vars']) + c = config.Config(schema=( + ('theme', config_options.Theme(default='mkdocs')), + ('theme_dir', config_options.ThemeDir(exists=True)), + )) + c.load_dict(config_contents) + errors, warnings = c.validate() + self.assertEqual(len(errors), 0) + self.assertEqual(c['theme'].dirs, result['dirs']) + self.assertEqual(c['theme'].static_templates, set(result['static_templates'])) + self.assertEqual(dict([(k, c['theme'][k]) for k in iter(c['theme'])]), result['vars']) def test_default_pages(self): - tmp_dir = tempfile.mkdtemp() - try: + with TemporaryDirectory() as tmp_dir: open(os.path.join(tmp_dir, 'index.md'), 'w').close() open(os.path.join(tmp_dir, 'about.md'), 'w').close() conf = config.Config(schema=config.DEFAULT_SCHEMA) @@ -187,12 +190,9 @@ def test_default_pages(self): }) conf.validate() self.assertEqual(['index.md', 'about.md'], conf['pages']) - finally: - shutil.rmtree(tmp_dir) def test_default_pages_nested(self): - tmp_dir = tempfile.mkdtemp() - try: + with TemporaryDirectory() as tmp_dir: open(os.path.join(tmp_dir, 'index.md'), 'w').close() open(os.path.join(tmp_dir, 'getting-started.md'), 'w').close() open(os.path.join(tmp_dir, 'about.md'), 'w').close() @@ -228,8 +228,6 @@ def test_default_pages_nested(self): os.path.join('subC', 'index.md') ]} ], conf['pages']) - finally: - shutil.rmtree(tmp_dir) def test_doc_dir_in_site_dir(self): diff --git a/mkdocs/tests/plugin_tests.py b/mkdocs/tests/plugin_tests.py index 6fa4091a6f..ac2c39833d 100644 --- a/mkdocs/tests/plugin_tests.py +++ b/mkdocs/tests/plugin_tests.py @@ -5,6 +5,7 @@ import unittest import mock +import os from mkdocs import plugins from mkdocs import utils @@ -14,7 +15,8 @@ class DummyPlugin(plugins.BasePlugin): config_scheme = ( ('foo', config.config_options.Type(utils.string_types, default='default foo')), - ('bar', config.config_options.Type(int, default=0)) + ('bar', config.config_options.Type(int, default=0)), + ('dir', config.config_options.Dir(exists=False)), ) def on_pre_page(self, content, **kwargs): @@ -29,18 +31,27 @@ def on_nav(self, item, **kwargs): class TestPluginClass(unittest.TestCase): def test_valid_plugin_options(self): + test_dir = 'test' options = { - 'foo': 'some value' + 'foo': 'some value', + 'dir': test_dir, } + cfg_fname = os.path.join('tmp', 'test', 'fname.yml') + cfg_fname = os.path.abspath(cfg_fname) + + cfg_dirname = os.path.dirname(cfg_fname) + expected = os.path.join(cfg_dirname, test_dir) + expected = { 'foo': 'some value', - 'bar': 0 + 'bar': 0, + 'dir': expected, } plugin = DummyPlugin() - errors, warnings = plugin.load_config(options) + errors, warnings = plugin.load_config(options, config_file_path=cfg_fname) self.assertEqual(plugin.config, expected) self.assertEqual(errors, []) self.assertEqual(warnings, []) @@ -132,7 +143,8 @@ def test_plugin_config_without_options(self, mock_class): self.assertIsInstance(cfg['plugins']['sample'], plugins.BasePlugin) expected = { 'foo': 'default foo', - 'bar': 0 + 'bar': 0, + 'dir': None, } self.assertEqual(cfg['plugins']['sample'].config, expected) @@ -154,7 +166,8 @@ def test_plugin_config_with_options(self, mock_class): self.assertIsInstance(cfg['plugins']['sample'], plugins.BasePlugin) expected = { 'foo': 'foo value', - 'bar': 42 + 'bar': 42, + 'dir': None, } self.assertEqual(cfg['plugins']['sample'].config, expected) @@ -194,7 +207,8 @@ def test_plugin_config_none_with_default(self, mock_class): self.assertIsInstance(cfg['plugins']['sample'], plugins.BasePlugin) expected = { 'foo': 'default foo', - 'bar': 0 + 'bar': 0, + 'dir': None, } self.assertEqual(cfg['plugins']['sample'].config, expected) diff --git a/tox.ini b/tox.ini index 52d0d7e0e1..bee768b2d5 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - py{27,34,35,36}-{unittests,integration,min-req}, + py{27,34,35,36,py,py3}-{unittests,integration,min-req}, flake8, markdown-lint, linkchecker, jshint, csslint [testenv] @@ -9,6 +9,7 @@ deps= py{27,34,35,36,py,py3}-{unittests,integration}: -rrequirements/project.txt py{27,34,35,36,py,py3}-min-req: -rrequirements/project-min.txt py{27,34,35,36,py,py3}-{unittests,min-req}: -rrequirements/test.txt + py{27,py}-{unittests,min-req}: backports.tempfile commands= {envpython} --version py{27,34,35,36,py,py3}-{unittests,min-req}: {envbindir}/nosetests --with-coverage --cover-package mkdocs mkdocs From d50468926f2cd1204d6aab0fc54e6b13c03b1591 Mon Sep 17 00:00:00 2001 From: Benjamin Gorman <8076bgorman@gmail.com> Date: Wed, 4 Apr 2018 16:59:46 +0100 Subject: [PATCH 034/342] Use .svg badges in README instead of .png (#1423) --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 98211a1271..24eccc1844 100644 --- a/README.md +++ b/README.md @@ -22,15 +22,15 @@ Project documentation with Markdown. Everyone interacting in the MkDocs project's codebases, issue trackers, chat rooms, and mailing lists is expected to follow the [PyPA Code of Conduct]. -[appveyor-image]: https://img.shields.io/appveyor/ci/d0ugal/mkdocs/master.png +[appveyor-image]: https://img.shields.io/appveyor/ci/d0ugal/mkdocs/master.svg [appveyor-link]: https://ci.appveyor.com/project/d0ugal/mkdocs [codecov-image]: http://codecov.io/github/mkdocs/mkdocs/coverage.svg?branch=master [codecov-link]: http://codecov.io/github/mkdocs/mkdocs?branch=master -[landscape-image]: https://landscape.io/github/mkdocs/mkdocs/master/landscape.svg?style=flat-square +[landscape-image]: https://landscape.io/github/mkdocs/mkdocs/master/landscape.svg?style=flat [landscape-link]: https://landscape.io/github/mkdocs/mkdocs/master -[pypi-v-image]: https://img.shields.io/pypi/v/mkdocs.png +[pypi-v-image]: https://img.shields.io/pypi/v/mkdocs.svg [pypi-v-link]: https://pypi.python.org/pypi/mkdocs -[travis-image]: https://img.shields.io/travis/mkdocs/mkdocs/master.png +[travis-image]: https://img.shields.io/travis/mkdocs/mkdocs/master.svg [travis-link]: https://travis-ci.org/mkdocs/mkdocs [mkdocs]: http://www.mkdocs.org From a43163fc8df0c27cdf8ba5c7138fffa1340e7d72 Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Wed, 4 Apr 2018 15:29:13 -0400 Subject: [PATCH 035/342] Document deploying to GH User/Org Pages. Fixes #1291 and made possable by the work in #1376. --- docs/user-guide/deploying-your-docs.md | 66 +++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/docs/user-guide/deploying-your-docs.md b/docs/user-guide/deploying-your-docs.md index 2c14f5e6e5..b06accd221 100644 --- a/docs/user-guide/deploying-your-docs.md +++ b/docs/user-guide/deploying-your-docs.md @@ -7,17 +7,25 @@ A basic guide to deploying your docs to various hosting providers ## GitHub Pages If you host the source code for a project on [GitHub], you can easily use -[GitHub Pages] to host the documentation for your project. After you `checkout` -the primary working branch (usually `master`) of the git repository where you +[GitHub Pages] to host the documentation for your project. There are two basic +types of GitHub Pages sites: [Project Pages] sites, and [User and Organization +Pages] sites. They are nearly identical but have some important differences, +which require a different work flow when deploying. + +### Project Pages + +Project Pages sites are simpler as the site files get deployed to a branch +within the project repository (`gh-pages` by default). After you `checkout` the +primary working branch (usually `master`) of the git repository where you maintain the source documentation for your project, run the following command: ```sh mkdocs gh-deploy ``` -That's it! Behind the scenes, MkDocs will build your docs and use the [ghp-import] -tool to commit them to the gh-pages branch and push the gh-pages branch to -GitHub. +That's it! Behind the scenes, MkDocs will build your docs and use the +[ghp-import] tool to commit them to the `gh-pages` branch and push the +`gh-pages` branch to GitHub. Use `mkdocs gh-deploy --help` to get a full list of options available for the `gh-deploy` command. @@ -29,12 +37,56 @@ files locally. !!! warning - You should never edit files in your gh-pages branch by hand if you're using - the `gh-deploy` command because you will lose your work. + You should never edit files in your `gh-pages` branch by hand if you're + using the `gh-deploy` command because you will lose your work. + +### Organization and User Pages + +User and Organization Pages sites are not tied to a specific project, and the +site files are deployed to the `master` branch in a dedicated repository named +with the GitHub account name. Therefore, you need working copies of two +repositories on our local system. For example, consider the following file +structure: + +```no-highlight +my-project/ + mkdocs.yml + docs/ +orgname.github.io/ +``` + +After making and verifying updates to your project you need to change +directories to the `orgname.github.io` repository and call the +`mkdocs gh-deploy` command from there: + +```sh +cd ../orgname.github.io/ +mkdocs gh-deploy --config-file ../my-project/mkdocs.yml --remote-branch master +``` + +Note that you need to explicitly point to the `mkdocs.yml` configuration file as +it is no longer in the current working directory. You also need to inform the +deploy script to commit to the `master` branch. You may override the default +with the [remote_branch] configuration setting, but if you forget to change +directories before running the deploy script, it will commit to the `master` +branch of your project, which you probably don't want. + +Be aware that you will not be able to review the built site before it is pushed +to GitHub. Therefore, you may want to verify any changes you make to the docs +beforehand by using the `build` or `serve` commands and reviewing the built +files locally. + +!!! warning + + You should never edit files in your pages repository by hand if you're + using the `gh-deploy` command because you will lose your work. [GitHub]: https://github.com/ [GitHub Pages]: https://pages.github.com/ +[Project Pages]: https://help.github.com/articles/user-organization-and-project-pages/#project-pages-sites +[User and Organization Pages]: https://help.github.com/articles/user-organization-and-project-pages/#user-and-organization-pages-sites [ghp-import]: https://github.com/davisp/ghp-import +[remote_branch]: ./configuration.md#remote_branch ## Read the Docs From d4c5832876accc7e59293852d86810585b97674c Mon Sep 17 00:00:00 2001 From: Christian Oliff Date: Thu, 5 Apr 2018 21:56:21 +0900 Subject: [PATCH 036/342] Update Font Awesome to 4.7.0 (MkDocs Theme) (#1447) Fixes #1438 --- .csslintrc | 2 +- mkdocs/themes/mkdocs/base.html | 2 +- .../themes/mkdocs/css/font-awesome-4.5.0.css | 4 - mkdocs/themes/mkdocs/css/font-awesome.min.css | 4 + mkdocs/themes/mkdocs/fonts/FontAwesome.otf | Bin 109688 -> 134808 bytes .../mkdocs/fonts/fontawesome-webfont.eot | Bin 70807 -> 165742 bytes .../mkdocs/fonts/fontawesome-webfont.svg | 3320 +++++++++++++---- .../mkdocs/fonts/fontawesome-webfont.ttf | Bin 142072 -> 165548 bytes .../mkdocs/fonts/fontawesome-webfont.woff | Bin 83588 -> 98024 bytes .../mkdocs/fonts/fontawesome-webfont.woff2 | Bin 66624 -> 77160 bytes 10 files changed, 2674 insertions(+), 658 deletions(-) delete mode 100644 mkdocs/themes/mkdocs/css/font-awesome-4.5.0.css create mode 100644 mkdocs/themes/mkdocs/css/font-awesome.min.css diff --git a/.csslintrc b/.csslintrc index 76f68b04c3..bdd8ef05a0 100644 --- a/.csslintrc +++ b/.csslintrc @@ -1,6 +1,6 @@ --exclude-list = mkdocs/themes/mkdocs/css/bootstrap-custom.min.css, mkdocs/themes/readthedocs/css/bootstrap-custom.min.css, - mkdocs/themes/mkdocs/css/font-awesome-4.5.0.css, + mkdocs/themes/mkdocs/css/font-awesome.min.css, mkdocs/themes/readthedocs/css/font-awesome-4.5.0.css, mkdocs/themes/mkdocs/css/highlight.css, mkdocs/themes/readthedocs/css/highlight.css, diff --git a/mkdocs/themes/mkdocs/base.html b/mkdocs/themes/mkdocs/base.html index ebe7c1939d..4c5f225093 100644 --- a/mkdocs/themes/mkdocs/base.html +++ b/mkdocs/themes/mkdocs/base.html @@ -18,7 +18,7 @@ {%- block styles %} - + {%- for path in extra_css %} diff --git a/mkdocs/themes/mkdocs/css/font-awesome-4.5.0.css b/mkdocs/themes/mkdocs/css/font-awesome-4.5.0.css deleted file mode 100644 index d0603cb4b0..0000000000 --- a/mkdocs/themes/mkdocs/css/font-awesome-4.5.0.css +++ /dev/null @@ -1,4 +0,0 @@ -/*! - * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.5.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.5.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.5.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.5.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"} diff --git a/mkdocs/themes/mkdocs/css/font-awesome.min.css b/mkdocs/themes/mkdocs/css/font-awesome.min.css new file mode 100644 index 0000000000..540440ce89 --- /dev/null +++ b/mkdocs/themes/mkdocs/css/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} diff --git a/mkdocs/themes/mkdocs/fonts/FontAwesome.otf b/mkdocs/themes/mkdocs/fonts/FontAwesome.otf index 3ed7f8b48ad9bfab52eb03822fefcd6b77d2e680..401ec0f36e4f73b8efa40bd6f604fe80d286db70 100644 GIT binary patch delta 72698 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 bMM{<8r5o_QP+BrH=a2Fd8Qd4nMDO|^^30|p delta 47438 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 ZgkrFCs-@$D%;rjbx%R0L9`($_ZvpHJ=LG-& diff --git a/mkdocs/themes/mkdocs/fonts/fontawesome-webfont.eot b/mkdocs/themes/mkdocs/fonts/fontawesome-webfont.eot index 9b6afaedc0fd7aaf927a07f82da9c11022251b8b..e9f60ca953f93e35eab4108bd414bc02ddcf3928 100644 GIT binary patch literal 165742 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 G*yFsbY~cLNXLP73aA74Mq6M9f^&YV`isWW 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
  • {{ nav_item.title }}
  • {% else %} - {% endif %} {% endfor %} @@ -161,9 +185,6 @@ navigation as a nested list. {% endif %} ``` -The `nav` object also contains a `homepage` object, which points to the `page` -object of the homepage. For example, you may want to access `nav.homepage.url`. - #### base_url The `base_url` provides a relative path to the root of the MkDocs project. @@ -175,22 +196,6 @@ folder on all pages you would do this: ``` -#### extra_css - -Contains a list of URLs to the style-sheets listed in the [extra_css] -config setting. Unlike the config setting, which contains local paths, this -variable contains absolute paths from the homepage. - -[extra_css]: configuration.md#extra_css - -#### extra_javascript - -Contains a list of URLs to the scripts listed in the [extra_javascript] config -setting. Unlike the config setting, which contains local paths, this variable -contains absolute paths from the homepage. - -[extra_javascript]: configuration.md#extra_javascript - #### mkdocs_version Contains the current MkDocs version. @@ -201,11 +206,23 @@ A Python datetime object that represents the date and time the documentation was built in UTC. This is useful for showing how recently the documentation was updated. +#### pages + +A list of [page](#page) objects including *all* pages in the project. The list +is a flat list with all pages sorted alphanumerically by directory and file +name. Note that index pages sort to the top within a directory. This list can +contain pages not included in the global [navigation](#nav) and may not match +the order of pages within that navigation. + #### page In templates which are not rendered from a Markdown source file, the `page` variable is `None`. In templates which are rendered from a Markdown source file, -the `page` variable contains a page object with the following attributes: +the `page` variable contains a `page` object. The same `page` objects are used +as `page` [navigation objects](#navigation-objects) in the global +[navigation](#nav) and in the [pages](#pages) template variable. + +All `page` objects contain the following attributes: ##### page.title @@ -257,19 +274,43 @@ documentation page. {% endfor %} ``` -##### page.canonical_url +##### page.url -The full, canonical URL to the current page. This includes the `site_url` from -the configuration. +The URL of the page relative to the MkDocs `site_dir`. It is expected that this +be used with [base_url] to ensure the URL is relative to the current page. -##### page.edit_url +```django +{{ page.title }} +``` -The full URL to the input page in the source repository. Typically used to -provide a link to edit the source page. +[base_url]: #base_url -##### page.url +##### page.abs_url + +The absolute URL of the page from the server root as determined by the value +assigned to the [site_url] configuration setting. The value includes any +subdirectory included in the `site_url`, but not the domain. [base_url] should +not be used with this variable. + +For example, if `site_url: http://example.com/`, then the value of +`page.abs_url` for the page `foo.md` would be `/foo/`. However, if +`site_url: http://example.com/bar/`, then the value of `page.abs_url` for the +page `foo.md` would be `/bar/foo/`. + +[site_url]: ./configuration.md#site_url + +##### page.canonical_url + +The full, canonical URL to the current page as determined by the value assigned +to the [site_url] configuration setting. The value includes the domain and any +subdirectory included in the `site_url`. [base_url] should not be used with this +variable. + +##### page.edit_url -The URL to the current page not including the `site_url` from the configuration. +The full URL to the source page in the source repository. Typically used to +provide a link to edit the source page. [base_url] should not be used with this +variable. ##### page.is_homepage @@ -284,12 +325,143 @@ on the homepage: ##### page.previous_page -The page object for the previous page. The usage is the same as for -`page`. +The page object for the previous page or `None`. The value will be `None` if the +current page is the first item in the site navigation or if the current page is +not included in the navigation at all. When the value is a page object, the +usage is the same as for `page`. ##### page.next_page -The page object for the next page.The usage is the same as for `page`. +The page object for the next page or `None`. The value will be `None` if the +current page is the last item in the site navigation or if the current page is +not included in the navigation at all. When the value is a page object, the +usage is the same as for `page`. + +##### page.parent + +The immediate parent of the page in the [site navigation](#nav). `None` if the +page is at the top level. + +##### page.children + +Pages do not contain children and the attribute is always `None`. + +##### page.active + +When `True`, indicates that this page is the currently viewed page. Defaults +to `False`. + +##### page.is_section + +Indicates that the navigation object is a "section" object. Always `False` for +page objects. + +##### page.is_page + +Indicates that the navigation object is a "page" object. Always `True` for +page objects. + +##### page.is_link + +Indicates that the navigation object is a "link" object. Always `False` for +page objects. + +### Navigation Objects + +Navigation objects contained in the [nav](#nav) template variable may be one of +[section](#section) objects, [page](#page) objects, and [link](#link) objects. +While section objects may contain nested navigation objects, pages and links do +not. + +Page objects are the full page object as used for the current [page](#page) with +all of the same attributes available. Section and Link objects contain a subset +of those attributes as defined below: + +#### Section + +A `section` navigation object defines a named section in the navigation and +contains a list of child navigation objects. Note that sections do not contain +URLs and are not links of any kind. However, by default, MkDocs sorts index +pages to the top and the first child might be used as the URL for a section if a +theme choses to do so. + + The following attributes are available on `section` objects: + +##### section.title + +The title of the section. + +##### section.parent + +The immediate parent of the section or `None` if the section is at the top +level. + +##### section.children + +An iterable of all child navigation objects. Children may include nested +sections, pages and links. + +##### section.active + +When `True`, indicates that a child page of this section is the current page and +can be used to highlight the section as the currently viewed section. Defaults +to `False`. + +##### section.is_section + +Indicates that the navigation object is a "section" object. Always `True` for +section objects. + +##### section.is_page + +Indicates that the navigation object is a "page" object. Always `False` for +section objects. + +##### section.is_link + +Indicates that the navigation object is a "link" object. Always `False` for +section objects. + +#### Link + +A `link` navigation object contains a link which does not point to an internal +MkDocs page. The following attributes are available on `link` objects: + +##### link.title + +The title of the link. This would generally be used as the label of the link. + +##### link.url + +The URL that the link points to. The URL should always be an absolute URLs and +should not need to have `base_url` prepened. + +##### link.parent + +The immediate parent of the link. `None` if the link is at the top level. + +##### link.children + +Links do not contain children and the attribute is always `None`. + +##### link.active + +External links cannot be "active" and the attribute is always `False`. + +##### link.is_section + +Indicates that the navigation object is a "section" object. Always `False` for +link objects. + +##### link.is_page + +Indicates that the navigation object is a "page" object. Always `False` for +link objects. + +##### link.is_link + +Indicates that the navigation object is a "link" object. Always `True` for +link objects. ### Extra Context @@ -607,4 +779,4 @@ For a much more detailed guide, see the official Python packaging documentation for [Packaging and Distributing Projects]. [Packaging and Distributing Projects]: https://packaging.python.org/en/latest/distributing/ -[theme]: ./configuration/#theme +[theme]: ./configuration.md#theme diff --git a/docs/user-guide/deploying-your-docs.md b/docs/user-guide/deploying-your-docs.md index b06accd221..5709b6f6d5 100644 --- a/docs/user-guide/deploying-your-docs.md +++ b/docs/user-guide/deploying-your-docs.md @@ -108,7 +108,7 @@ public repository. [rtd]: https://readthedocs.org/ [instructions]: https://read-the-docs.readthedocs.io/en/latest/getting_started.html#in-markdown [features]: http://read-the-docs.readthedocs.io/en/latest/features.html -[theme]: /user-guide/styling-your-docs.md +[theme]: ./styling-your-docs.md#readthedocs ## Other Providers @@ -153,4 +153,4 @@ deploying to [GitHub](#github-pages) but only on a custom domain. Other web servers may be configured to use it but the feature won't always be available. See the documentation for your server of choice for more information. -[site_dir]: ./configuration/#site_dir +[site_dir]: ./configuration.md#site_dir diff --git a/docs/user-guide/plugins.md b/docs/user-guide/plugins.md index fbf53d3c8d..d1b8fc451f 100644 --- a/docs/user-guide/plugins.md +++ b/docs/user-guide/plugins.md @@ -149,7 +149,7 @@ entire site. : The `serve` event is only called when the `serve` command is used during development. It is passed the `Server` instance which can be modified before it is activated. For example, additional files or directories could be added - to the list of "watched" filed for auto-reloading. + to the list of "watched" files for auto-reloading. Parameters: : __server:__ `livereload.Server` instance @@ -178,14 +178,30 @@ entire site. Parameters: : __config:__ global configuration object +##### on_files + +: The `files` event is called after the files collection is populated from the + `docs_dir`. Use this event to add, remove, or alter files in the + collection. Note that Page objects have not yet been associated with the + file objects in the collection. Use [Page Events] to manipulate page + specific data. + + Parameters: + : __files:__ global files collection + : __config:__ global configuration object + + Returns: + : global files collection + ##### on_nav : The `nav` event is called after the site navigation is created and can be used to alter the site navigation. Parameters: - : __site_navigation:__ global navigation object + : __nav:__ global navigation object : __config:__ global configuration object + : __files:__ global files collection Returns: : global navigation object @@ -263,8 +279,8 @@ called after the [env] event and before any [page events]. #### Page Events Page events are called once for each Markdown page included in the site. All -page events are called after the [post_template] event and before the [post_build] -event. +page events are called after the [post_template] event and before the +[post_build] event. ##### on_pre_page diff --git a/docs/user-guide/styling-your-docs.md b/docs/user-guide/styling-your-docs.md index c97abf99d1..cae1d8f244 100644 --- a/docs/user-guide/styling-your-docs.md +++ b/docs/user-guide/styling-your-docs.md @@ -6,9 +6,8 @@ How to style and theme your documentation. MkDocs includes a couple [built-in themes] as well as various [third party themes], all of which can easily be customized with [extra CSS or -JavaScript][docs_dir] or overridden from the [theme directory][theme_dir]. You -can also create your own [custom theme] from the ground up for your -documentation. +JavaScript][docs_dir] or overridden from the theme's [custom_dir]. You can also +create your own [custom theme] from the ground up for your documentation. To use a theme that is included in MkDocs, simply add this to your `mkdocs.yml` config file. @@ -261,21 +260,21 @@ any additional CSS files included in the `custom_dir`. [browse source]: https://github.com/mkdocs/mkdocs/tree/master/mkdocs/themes/mkdocs [built-in themes]: #built-in-themes [Bootstrap]: http://getbootstrap.com/ -[theme configuration options]: configuration.md#theme +[theme configuration options]: ./configuration.md#theme [Read the Docs]: https://readthedocs.org/ [community wiki]: https://github.com/mkdocs/mkdocs/wiki/MkDocs-Themes [custom theme]: ./custom-themes.md [customize]: #customizing-a-theme [docs_dir]: #using-the-docs_dir -[documentation directory]: ./configuration/#docs_dir +[documentation directory]: ./configuration.md#docs_dir [extra_css]: ./configuration.md#extra_css [extra_javascript]: ./configuration.md#extra_javascript [Jinja documentation]: http://jinja.pocoo.org/docs/dev/templates/#template-inheritance [mkdocs]: #mkdocs [ReadTheDocs]: ./deploying-your-docs.md#readthedocs [Template Variables]: ./custom-themes.md#template-variables -[custom_dir]: ./configuration/#custom_dir -[name]: ./configuration/#name +[custom_dir]: ./configuration.md#custom_dir +[name]: ./configuration.md#name [third party themes]: #third-party-themes [super block]: http://jinja.pocoo.org/docs/dev/templates/#super-blocks [base_url]: ./custom-themes.md#base_url diff --git a/docs/user-guide/writing-your-docs.md b/docs/user-guide/writing-your-docs.md index 91fde615d3..15eec890a3 100644 --- a/docs/user-guide/writing-your-docs.md +++ b/docs/user-guide/writing-your-docs.md @@ -22,7 +22,8 @@ docs/ By convention your project homepage should always be named `index`. Any of the following extensions may be used for your Markdown source files: `markdown`, -`mdown`, `mkdn`, `mkd`, `md`. +`mdown`, `mkdn`, `mkd`, `md`. All Markdown files included in your documentation +directory will be rendered in the built site regardless of any settings. You can also create multi-page documentation, by creating several Markdown files: @@ -65,55 +66,84 @@ nested URLs, like so: /license/ ``` +### Index pages + +When a directory is requested, by default, most web servers will return an index +file (usually named `index.html`) contained within that directory if one exists. +For that reason, the homepage in all of the examples above has been named +`index.md`, which MkDocs will render to `index.html` when building the site. + +Many repository hosting sites provide special treatment for README files by +displaying the contents of the README file when browsing the contents of a +directory. Therefore, MkDocs will allow you to name your index pages as +`README.md` instead of `index.md`. In that way, when users are browsing your +source code, the repository host can display the index page of that directory as +it is a README file. However, when MkDocs renders your site, the file will be +renamed to `index.html` so that the server will serve it as a proper index file. + +You should not include both an `index.md` file and a `README.md` file in the +same directory. It is suggested that you chose a convention for your project and +then stick to it. + ### Configure Pages and Navigation -The [pages configuration](configuration.md#pages) in your `mkdocs.yml` defines -which pages are built by MkDocs and how they appear in the documentation -navigation. If not provided, the pages configuration will be automatically -created by discovering all the Markdown files in the [documentation -directory](configuration.md#docs_dir). An automatically created pages -configuration will always be sorted alphanumerically by file name. You will need -to manually define your pages configuration if you would like your pages sorted -differently. +The [nav](configuration.md#nav) configuration setting in your `mkdocs.yml` file +defines which pages are included in the global site navigation menu as well as +the structure of that menu. If not provided, the navigation will be +automatically created by discovering all the Markdown files in the +[documentation directory](configuration.md#docs_dir). An automatically created +navigation configuration will always be sorted alphanumerically by file name +(except that index files will always be listed first within a sub-section). You +will need to manually define your navigation configuration if you would like +your navigation menu sorted differently. -A simple pages configuration looks like this: +A simple navigation configuration looks like this: ```no-highlight -pages: +nav: - 'index.md' - 'about.md' ``` -With this example we will build two pages at the top level and they will -automatically have their titles inferred from the filename. Assuming `docs_dir` -has the default value, `docs`, the source files for this documentation would be -`docs/index.md` and `docs/about.md`. To provide a custom name for these pages, -they can be added before the filename. +All paths in the navigation configuration must be relative to the `docs_dir` +configuration option. If that option is set to the default value, `docs`, the +source files for the above configuration would be located at `docs/index.md` and +`docs/about.md`. + +The above example will result in two navigation items being created at the top +level and with their titles inferred from the contents of the file (or the +filename if no title is defined within the file). To define a custom title for +the pages, the title can be added before the filename. ```no-highlight -pages: +nav: - Home: 'index.md' - About: 'about.md' ``` -Subsections can be created by listing related pages together under a section -title. For example: +Note that if a title is defined for a page in the navigation, that title will be +used throughout the site for that page and will override any title defined +within the page itself. + +Navigation sub-sections can be created by listing related pages together under a +section title. For example: ```no-highlight -pages: +nav: - Home: 'index.md' - User Guide: - - 'Writing your docs': 'user-guide/writing-your-docs.md' - - 'Styling your docs': 'user-guide/styling-your-docs.md' + - 'Writing your docs': 'writing-your-docs.md' + - 'Styling your docs': 'styling-your-docs.md' - About: - - 'License': 'about/license.md' - - 'Release Notes': 'about/release-notes.md' + - 'License': 'license.md' + - 'Release Notes': 'release-notes.md' ``` -With the above configuration we have three top level sections: Home, User Guide -and About. Then under User Guide we have two pages, Writing your docs and -Styling your docs. Under the About section we also have two pages, License and -Release Notes. +With the above configuration we have three top level items: "Home", "User Guide" +and "About." "Home" is a link to the homepage for the site. Under the "User +Guide" section two pages are listed: "Writing your docs" and "Styling your +docs." Under the "About" section two more pages are listed: "License" and +"Release Notes." Note that a section cannot have a page assigned to it. Sections are only containers for child pages and sub-sections. You may nest sections as deeply as @@ -121,6 +151,11 @@ you like. However, be careful that you don't make it too difficult for your users to navigate through the site navigation by over-complicating the nesting. While sections may mirror your directly structure, they do not have to. +Any pages not listed in your navigation configuration will still be rendered and +included with the built site, however, they will not be linked from the global +navigation and will not be included in the `previous` and `next` links. Such +pages will be "hidden" unless linked to directly. + ## Writing with Markdown MkDocs pages must be authored in [Markdown][md], a lightweight markup language @@ -139,7 +174,7 @@ configuration setting for details on how to enable extensions. MkDocs includes some extensions by default, which are highlighted below. [Python-Markdown]: https://python-markdown.github.io/ -[md]: http://daringfireball.net/projects/markdown/ +[md]: https://daringfireball.net/projects/markdown/ [differences]: https://python-markdown.github.io/#differences [syntax]: https://daringfireball.net/projects/markdown/syntax [extensions]: https://python-markdown.github.io/extensions/ @@ -149,7 +184,7 @@ MkDocs includes some extensions by default, which are highlighted below. MkDocs allows you to interlink your documentation by using regular Markdown [links]. However, there are a few additional benefits to formatting those links -specifically for MkDocs as outlines below. +specifically for MkDocs as outlined below. [links]: https://daringfireball.net/projects/markdown/syntax#link @@ -280,6 +315,15 @@ also be previewed if you're working on the documentation with a Markdown editor. [GitHub pages CNAME file]: https://help.github.com/articles/using-a-custom-domain-with-github-pages/ +#### Linking from raw HTML + +Markdown allows document authors to fall back to raw HTML when the Markdown +syntax does not meets the author's needs. MkDocs does not limit Markdown in this +regard. However, as all raw HTML is ignored by the Markdown parser, MkDocs is +not able to validate or convert links contained in raw HTML. When including +internal links within raw HTML, you will need to manually format the link +appropriately for the rendered document. + ### Meta-Data MkDocs includes support for [MultiMarkdown] style meta-data (often called @@ -341,7 +385,7 @@ specific page. The following keys are supported: MkDocs will attempt to determine the title of a document in the following ways, in order: - 1. A title defined in the [pages] configuration setting for a document. + 1. A title defined in the [nav] configuration setting for a document. 2. A title defined in the `title` meta-data key of a document. 3. A level 1 Markdown header on the first line of the document body. 4. The filename of a document. @@ -350,7 +394,7 @@ specific page. The following keys are supported: additional sources in the above list. [MultiMarkdown]: http://fletcherpenney.net/MultiMarkdown_Syntax_Guide#metadata -[pages]: configuration.md#pages +[nav]: configuration.md#nav ### Tables diff --git a/mkdocs.yml b/mkdocs.yml index ae1e80ca40..8bd62127dd 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -6,7 +6,7 @@ site_author: MkDocs Team repo_url: https://github.com/mkdocs/mkdocs/ edit_uri: "" -pages: +nav: - Home: index.md - User Guide: - Writing Your Docs: user-guide/writing-your-docs.md diff --git a/mkdocs/commands/build.py b/mkdocs/commands/build.py index d8a5cd27d6..df31d6efc9 100644 --- a/mkdocs/commands/build.py +++ b/mkdocs/commands/build.py @@ -3,15 +3,17 @@ from __future__ import unicode_literals from datetime import datetime from calendar import timegm -import io import logging import os import gzip +import io from jinja2.exceptions import TemplateNotFound import jinja2 -from mkdocs import nav, utils +from mkdocs import utils +from mkdocs.structure.files import get_files +from mkdocs.structure.nav import get_navigation import mkdocs @@ -30,18 +32,17 @@ def filter(self, record): log.addFilter(DuplicateFilter()) -def get_context(nav, config, page=None): +def get_context(nav, files, config, page=None, base_url=''): """ - Given the SiteNavigation and config, generate the context which is relevant - to app pages. + Return the template context for a given page or template. """ - if nav is None: - return {'page', page} + if page is not None: + base_url = utils.get_relative_url('.', page.url) - extra_javascript = utils.create_media_urls(nav, config['extra_javascript']) + extra_javascript = utils.create_media_urls(config['extra_javascript'], page, base_url) - extra_css = utils.create_media_urls(nav, config['extra_css']) + extra_css = utils.create_media_urls(config['extra_css'], page, base_url) # Support SOURCE_DATE_EPOCH environment variable for "reproducible" builds. # See https://reproducible-builds.org/specs/source-date-epoch/ @@ -49,8 +50,9 @@ def get_context(nav, config, page=None): return { 'nav': nav, - # base_url should never end with a slash. - 'base_url': nav.url_context.make_relative('/').rstrip('/'), + 'pages': files.documentation_pages(), + + 'base_url': base_url.rstrip('/'), 'extra_css': extra_css, 'extra_javascript': extra_javascript, @@ -63,192 +65,169 @@ def get_context(nav, config, page=None): } -def build_template(template_name, env, config, site_navigation=None): - """ Build a template using the theme environment. """ - - log.debug("Building template: %s", template_name) - - try: - template = env.get_template(template_name) - except TemplateNotFound: - log.info("Template skipped: '{}'. Not found in template directories.".format(template_name)) - return +def _build_template(name, template, files, config, nav): + """ + Return rendered output for given template as a string. + """ # Run `pre_template` plugin events. template = config['plugins'].run_event( - 'pre_template', template, template_name=template_name, config=config + 'pre_template', template, template_name=name, config=config ) - context = get_context(site_navigation, config) + if utils.is_error_template(name): + # Force absolute URLs in the nav of error pages and account for the + # possability that the docs root might be different than the server root. + # See https://github.com/mkdocs/mkdocs/issues/77 + base_url = utils.urlparse(config['site_url']).path + else: + base_url = utils.get_relative_url('.', name) + + context = get_context(nav, files, config, base_url=base_url) # Run `template_context` plugin events. context = config['plugins'].run_event( - 'template_context', context, template_name=template_name, config=config + 'template_context', context, template_name=name, config=config ) - output_content = template.render(context) + output = template.render(context) # Run `post_template` plugin events. - output_content = config['plugins'].run_event( - 'post_template', output_content, template_name=template_name, config=config + output = config['plugins'].run_event( + 'post_template', output, template_name=name, config=config ) - if output_content.strip(): - output_path = os.path.join(config['site_dir'], template_name) - utils.write_file(output_content.encode('utf-8'), output_path) + return output - if template_name == 'sitemap.xml': - log.debug("Gzipping template: %s", template_name) - with gzip.open('{}.gz'.format(output_path), 'wb') as f: - f.write(output_content.encode('utf-8')) - else: - log.info("Template skipped: '{}'. Generated empty output.".format(template_name)) +def _build_theme_template(template_name, env, files, config, nav): + """ Build a template using the theme environment. """ -def build_error_template(template, env, config, site_navigation): - """ - Build error template. + log.debug("Building theme template: {}".format(template_name)) - Force absolute URLs in the nav of error pages and account for the - possability that the docs root might be different than the server root. - See https://github.com/mkdocs/mkdocs/issues/77 - """ + try: + template = env.get_template(template_name) + except TemplateNotFound: + log.warn("Template skipped: '{}' not found in theme directories.".format(template_name)) + return - site_navigation.url_context.force_abs_urls = True - default_base = site_navigation.url_context.base_path - site_navigation.url_context.base_path = utils.urlparse(config['site_url']).path + output = _build_template(template_name, template, files, config, nav) - build_template(template, env, config, site_navigation) + if output.strip(): + output_path = os.path.join(config['site_dir'], template_name) + utils.write_file(output.encode('utf-8'), output_path) - # Reset nav behavior to the default - site_navigation.url_context.force_abs_urls = False - site_navigation.url_context.base_path = default_base + if template_name == 'sitemap.xml': + log.debug("Gzipping template: %s", template_name) + with gzip.open('{}.gz'.format(output_path), 'wb') as f: + f.write(output.encode('utf-8')) + else: + log.info("Template skipped: '{}' generated empty output.".format(template_name)) -def _build_page(page, config, site_navigation, env, dirty=False): - """ Build a Markdown page and pass to theme template. """ +def _build_extra_template(template_name, files, config, nav): + """ Build user templates which are not part of the theme. """ - # Run the `pre_page` plugin event - page = config['plugins'].run_event( - 'pre_page', page, config=config, site_navigation=site_navigation - ) + log.debug("Building extra template: {}".format(template_name)) - page.read_source(config=config) + file = files.get_file_from_path(template_name) + if file is None: + log.warn("Template skipped: '{}' not found in docs_dir.".format(template_name)) + return - # Run `page_markdown` plugin events. - page.markdown = config['plugins'].run_event( - 'page_markdown', page.markdown, page=page, config=config, site_navigation=site_navigation - ) + try: + with io.open(file.abs_src_path, 'r', encoding='utf-8', errors='strict') as f: + template = jinja2.Template(f.read()) + except Exception as e: + log.warn("Error reading template '{}': {}".format(template_name, e)) + return - page.render(config, site_navigation) + output = _build_template(template_name, template, files, config, nav) - # Run `page_content` plugin events. - page.content = config['plugins'].run_event( - 'page_content', page.content, page=page, config=config, site_navigation=site_navigation - ) + if output.strip(): + utils.write_file(output.encode('utf-8'), file.abs_dest_path) + else: + log.info("Template skipped: '{}' generated empty output.".format(template_name)) - context = get_context(site_navigation, config, page) - # Allow 'template:' override in md source files. - if 'template' in page.meta: - template = env.get_template(page.meta['template']) - else: - template = env.get_template('main.html') +def _populate_page(page, config, files, dirty=False): + """ Read page content from docs_dir and render Markdown. """ - # Run `page_context` plugin events. - context = config['plugins'].run_event( - 'page_context', context, page=page, config=config, site_navigation=site_navigation - ) + try: + # When --dirty is used, only read the page if the file has been modified since the + # previous build of the output. + if dirty and not page.file.is_modified(): + return + + # Run the `pre_page` plugin event + page = config['plugins'].run_event( + 'pre_page', page, config=config, files=files + ) - # Render the template. - output_content = template.render(context) + page.read_source(config) - # Run `post_page` plugin events. - output_content = config['plugins'].run_event( - 'post_page', output_content, page=page, config=config - ) + # Run `page_markdown` plugin events. + page.markdown = config['plugins'].run_event( + 'page_markdown', page.markdown, page=page, config=config, files=files + ) - # Write the output file. - if output_content.strip(): - utils.write_file(output_content.encode('utf-8'), page.abs_output_path) - else: - log.info("Page skipped: '{}'. Generated empty output.".format(page.title)) + page.render(config, files) + # Run `page_content` plugin events. + page.content = config['plugins'].run_event( + 'page_content', page.content, page=page, config=config, files=files + ) + except Exception as e: + log.error("Error reading page '{}': {}".format(page.file.src_path, e)) + raise -def build_extra_templates(extra_templates, config, site_navigation=None): - """ Build user templates which are not part of the theme. """ - log.debug("Building extra_templates pages") +def _build_page(page, config, files, nav, env, dirty=False): + """ Pass a Page to theme template and write output to site_dir. """ - for extra_template in extra_templates: + try: + # When --dirty is used, only build the page if the file has been modified since the + # previous build of the output. + if dirty and not page.file.is_modified(): + return - input_path = os.path.join(config['docs_dir'], extra_template) + log.debug("Building page {}".format(page.file.src_path)) - with io.open(input_path, 'r', encoding='utf-8') as template_file: - template = jinja2.Template(template_file.read()) + # Activate page. Signals to theme that this is the current page. + page.active = True - # Run `pre_template` plugin events. - template = config['plugins'].run_event( - 'pre_template', template, template_name=extra_template, config=config - ) + context = get_context(nav, files, config, page) - context = get_context(site_navigation, config) + # Allow 'template:' override in md source files. + if 'template' in page.meta: + template = env.get_template(page.meta['template']) + else: + template = env.get_template('main.html') - # Run `template_context` plugin events. + # Run `page_context` plugin events. context = config['plugins'].run_event( - 'template_context', context, template_name=extra_template, config=config + 'page_context', context, page=page, config=config, nav=nav ) - output_content = template.render(context) + # Render the template. + output = template.render(context) - # Run `post_template` plugin events. - output_content = config['plugins'].run_event( - 'post_template', output_content, template_name=extra_template, config=config + # Run `post_page` plugin events. + output = config['plugins'].run_event( + 'post_page', output, page=page, config=config ) - if output_content.strip(): - output_path = os.path.join(config['site_dir'], extra_template) - utils.write_file(output_content.encode('utf-8'), output_path) + # Write the output file. + if output.strip(): + utils.write_file(output.encode('utf-8', errors='xmlcharrefreplace'), page.file.abs_dest_path) else: - log.info("Template skipped: '{}'. Generated empty output.".format(extra_template)) - - -def build_pages(config, dirty=False): - """ Build all pages and write them into the build directory. """ - - site_navigation = nav.SiteNavigation(config) + log.info("Page skipped: '{}'. Generated empty output.".format(page.file.src_path)) - # Run `nav` plugin events. - site_navigation = config['plugins'].run_event('nav', site_navigation, config=config) - - env = config['theme'].get_env() - - # Run `env` plugin events. - env = config['plugins'].run_event( - 'env', env, config=config, site_navigation=site_navigation - ) - - for template in config['theme'].static_templates: - if utils.is_error_template(template): - build_error_template(template, env, config, site_navigation) - else: - build_template(template, env, config, site_navigation) - - build_extra_templates(config['extra_templates'], config, site_navigation) - - log.debug("Building markdown pages.") - for page in site_navigation.walk_pages(): - try: - # When --dirty is used, only build the page if the markdown has been modified since the - # previous build of the output. - if dirty and (utils.modified_time(page.abs_input_path) < utils.modified_time(page.abs_output_path)): - continue - - log.debug("Building page %s", page.input_path) - _build_page(page, config, site_navigation, env) - except Exception: - log.error("Error building page %s", page.input_path) - raise + # Deactivate page + page.active = False + except Exception as e: + log.error("Error building page '{}': {}".format(page.file.src_path, e)) + raise def build(config, live_server=False, dirty=False): @@ -263,29 +242,54 @@ def build(config, live_server=False, dirty=False): if not dirty: log.info("Cleaning site directory") utils.clean_directory(config['site_dir']) - else: + else: # pragma: no cover # Warn user about problems that may occur with --dirty option log.warning("A 'dirty' build is being performed, this will likely lead to inaccurate navigation and other" " links within your site. This option is designed for site development purposes only.") - if not live_server: + if not live_server: # pragma: no cover log.info("Building documentation to directory: %s", config['site_dir']) if dirty and site_directory_contains_stale_files(config['site_dir']): log.info("The directory contains stale files. Use --clean to remove them.") - # Reversed as we want to take the media files from the builtin theme - # and then from the custom theme_dir so that the custom versions take - # precedence. - for theme_dir in reversed(config['theme'].dirs): - log.debug("Copying static assets from %s", theme_dir) - utils.copy_media_files( - theme_dir, config['site_dir'], exclude=['*.py', '*.pyc', '*.html', 'mkdocs_theme.yml'], dirty=dirty - ) + # First gather all data from all files/pages to ensure all data is consistent across all pages. + + files = get_files(config) + env = config['theme'].get_env() + files.add_files_from_theme(env, config) + + # Run `files` plugin events. + files = config['plugins'].run_event('files', files, config=config) + + nav = get_navigation(files, config) + + # Run `nav` plugin events. + nav = config['plugins'].run_event('nav', nav, config=config, files=files) - log.debug("Copying static assets from the docs dir.") - utils.copy_media_files(config['docs_dir'], config['site_dir'], dirty=dirty) + log.debug("Reading markdown pages.") + for file in files.documentation_pages(): + _populate_page(file.page, config, files, dirty) - build_pages(config, dirty=dirty) + # Run `env` plugin events. + env = config['plugins'].run_event( + 'env', env, config=config, files=files + ) + + # Start writing files to site_dir now that all data is gathered. Note that order matters. Files + # with lower precedence get written first so that files with higher precedence can overwrite them. + + log.debug("Copying static assets.") + files.copy_static_files(dirty=dirty) + + for template in config['theme'].static_templates: + _build_theme_template(template, env, files, config, nav) + + for template in config['extra_templates']: + _build_extra_template(template, files, config, nav) + + log.debug("Building markdown pages.") + for file in files.documentation_pages(): + _build_page(file.page, config, files, nav, env, dirty) # Run `post_build` plugin events. config['plugins'].run_event('post_build', config) diff --git a/mkdocs/config/base.py b/mkdocs/config/base.py index 60f9b587ab..b19550e3ff 100644 --- a/mkdocs/config/base.py +++ b/mkdocs/config/base.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals import logging import os +import sys from mkdocs import exceptions from mkdocs import utils @@ -28,6 +29,13 @@ def __init__(self, schema, config_file_path=None): self._schema = schema self._schema_keys = set(dict(schema).keys()) + # Ensure config_file_path is a Unicode string + if config_file_path is not None and not isinstance(config_file_path, utils.text_type): + try: + # Assume config_file_path is encoded with the file system encoding. + config_file_path = config_file_path.decode(encoding=sys.getfilesystemencoding()) + except UnicodeDecodeError: + raise ValidationError("config_file_path is not a Unicode string.") self.config_file_path = config_file_path self.data = {} diff --git a/mkdocs/config/config_options.py b/mkdocs/config/config_options.py index f410f72d5c..a631da7e66 100644 --- a/mkdocs/config/config_options.py +++ b/mkdocs/config/config_options.py @@ -1,8 +1,8 @@ from __future__ import unicode_literals -from collections import Sequence import os -from collections import namedtuple +import sys +from collections import Sequence, namedtuple import markdown from mkdocs import utils, theme, plugins @@ -294,31 +294,29 @@ class FilesystemObject(Type): def __init__(self, exists=False, **kwargs): super(FilesystemObject, self).__init__(type_=utils.string_types, **kwargs) self.exists = exists + self.config_dir = None def pre_validation(self, config, key_name): - value = config[key_name] - - if not value: - return - - if os.path.isabs(value): - return - - if config.config_file_path is None: - # Unable to determine absolute path of the config file; fall back - # to trusting the relative path - return - - config_dir = os.path.dirname(config.config_file_path) - value = os.path.join(config_dir, value) - config[key_name] = value + self.config_dir = os.path.dirname(config.config_file_path) if config.config_file_path else None def run_validation(self, value): value = super(FilesystemObject, self).run_validation(value) + # PY2 only: Ensure value is a Unicode string. On PY3 byte strings fail + # the type test (super.run_validation) so we never get this far. + if not isinstance(value, utils.text_type): + try: + # Assume value is encoded with the file system encoding. + value = value.decode(encoding=sys.getfilesystemencoding()) + except UnicodeDecodeError: + raise ValidationError("The path is not a Unicode string.") + if self.config_dir and not os.path.isabs(value): + value = os.path.join(self.config_dir, value) if self.exists and not self.existence_test(value): raise ValidationError("The path {path} isn't an existing {name}.". format(path=value, name=self.name)) - return os.path.abspath(value) + value = os.path.abspath(value) + assert isinstance(value, utils.text_type) + return value class Dir(FilesystemObject): @@ -392,6 +390,8 @@ def pre_validation(self, config, key_name): if config.get(key_name) is None: return + super(ThemeDir, self).pre_validation(config, key_name) + warning = ('The configuration option {0} has been deprecated and will ' 'be removed in a future release of MkDocs.') self.warnings.append(warning) @@ -518,15 +518,15 @@ def post_validation(self, config, key_name): ).format(key_name, "', '".join(actual_files))) -class Pages(OptionallyRequired): +class Nav(OptionallyRequired): """ - Pages Config Option + Nav Config Option - Validate the pages config. Automatically add all markdown files if empty. + Validate the Nav config. Automatically add all markdown files if empty. """ def __init__(self, **kwargs): - super(Pages, self).__init__(**kwargs) + super(Nav, self).__init__(**kwargs) self.file_match = utils.is_markdown_file def run_validation(self, value): @@ -546,42 +546,15 @@ def run_validation(self, value): config_types, {utils.text_type, dict} )) - def walk_docs_dir(self, docs_dir): - - if self.file_match is None: - raise StopIteration - - for (dirpath, dirs, filenames) in os.walk(docs_dir, followlinks=True): - dirs.sort() - for filename in sorted(filenames): - fullpath = os.path.join(dirpath, filename) - - # Some editors (namely Emacs) will create temporary symlinks - # for internal magic. We can just ignore these files. - if os.path.islink(fullpath): - local_fullpath = os.path.join(dirpath, os.readlink(fullpath)) - if not os.path.exists(local_fullpath): - continue - - relpath = os.path.normpath(os.path.relpath(fullpath, docs_dir)) - if self.file_match(relpath): - yield relpath - def post_validation(self, config, key_name): - - if config[key_name] is not None: - return - - pages = [] - - for filename in self.walk_docs_dir(config['docs_dir']): - - if os.path.splitext(filename)[0] == 'index': - pages.insert(0, filename) - else: - pages.append(filename) - - config[key_name] = utils.nest_paths(pages) + # TODO: remove this when `pages` config setting is fully deprecated. + if key_name == 'pages' and config['pages'] is not None: + if config['nav'] is None: + # copy `pages` config to new 'nav' config setting + config['nav'] = config['pages'] + warning = ("The 'pages' configuration option has been deprecated and will " + "be removed in a future release of MkDocs. Use 'nav' instead.") + self.warnings.append(warning) class Private(OptionallyRequired): diff --git a/mkdocs/config/defaults.py b/mkdocs/config/defaults.py index 2e93695893..99600b3bf7 100644 --- a/mkdocs/config/defaults.py +++ b/mkdocs/config/defaults.py @@ -19,9 +19,10 @@ # The title to use for the documentation ('site_name', config_options.Type(utils.string_types, required=True)), - # Defines the structure of the navigation and which markdown files are - # included in the build. - ('pages', config_options.Pages()), + # Defines the structure of the navigation. + ('nav', config_options.Nav()), + # TODO: remove this when the `pages` config setting is fully deprecated. + ('pages', config_options.Nav()), # The full URL to where the documentation will be hosted ('site_url', config_options.URL()), diff --git a/mkdocs/contrib/search/search_index.py b/mkdocs/contrib/search/search_index.py index 2b5587abc7..ca4eebcab2 100644 --- a/mkdocs/contrib/search/search_index.py +++ b/mkdocs/contrib/search/search_index.py @@ -69,17 +69,17 @@ def add_entry_from_context(self, page): # Get the absolute URL for the page, this is then # prepended to the urls of the sections - abs_url = page.abs_url + url = page.url # Create an entry for the full page. self._add_entry( title=page.title, text=self.strip_tags(page.content).rstrip('\n'), - loc=abs_url + loc=url ) for section in parser.data: - self.create_entry_for_section(section, page.toc, abs_url) + self.create_entry_for_section(section, page.toc, url) def create_entry_for_section(self, section, toc, abs_url): """ diff --git a/mkdocs/nav.py b/mkdocs/nav.py deleted file mode 100644 index a78ab588df..0000000000 --- a/mkdocs/nav.py +++ /dev/null @@ -1,416 +0,0 @@ -# coding: utf-8 - -""" -Deals with generating the site-wide navigation. - -This consists of building a set of interlinked page and header objects. -""" - -from __future__ import unicode_literals -import datetime -import logging -import markdown -import os -import io - -from mkdocs import utils, exceptions, toc -from mkdocs.utils import meta -from mkdocs.relative_path_ext import RelativePathExtension - -log = logging.getLogger(__name__) - - -def _filename_to_title(filename): - """ - Automatically generate a default title, given a filename. - """ - if utils.is_homepage(filename): - return 'Home' - - return utils.filename_to_title(filename) - - -@meta.transformer() -def default(value): - """ By default, return all meta values as strings. """ - return ' '.join(value) - - -class SiteNavigation(object): - def __init__(self, config): - self.url_context = URLContext() - self.file_context = FileContext() - self.nav_items, self.pages = _generate_site_navigation( - config, self.url_context) - self.homepage = self.pages[0] if self.pages else None - self.use_directory_urls = config['use_directory_urls'] - - def __str__(self): - return ''.join([str(item) for item in self]) - - def __iter__(self): - return iter(self.nav_items) - - def __len__(self): - return len(self.nav_items) - - def walk_pages(self): - """ - Returns each page in the site in turn. - - Additionally this sets the active status of the pages and headers, - in the site navigation, so that the rendered navbar can correctly - highlight the currently active page and/or header item. - """ - page = self.homepage - page.set_active() - self.url_context.set_current_url(page.abs_url) - self.file_context.set_current_path(page.input_path) - yield page - while page.next_page: - page.set_active(False) - page = page.next_page - page.set_active() - self.url_context.set_current_url(page.abs_url) - self.file_context.set_current_path(page.input_path) - yield page - page.set_active(False) - - @property - def source_files(self): - if not hasattr(self, '_source_files'): - self._source_files = set([page.input_path for page in self.pages]) - return self._source_files - - -class URLContext(object): - """ - The URLContext is used to ensure that we can generate the appropriate - relative URLs to other pages from any given page in the site. - - We use relative URLs so that static sites can be deployed to any location - without having to specify what the path component on the host will be - if the documentation is not hosted at the root path. - """ - - def __init__(self): - self.base_path = '/' - self.force_abs_urls = False - - def set_current_url(self, current_url): - self.base_path = os.path.dirname(current_url) - - def make_relative(self, url): - """ - Given a URL path return it as a relative URL, - given the context of the current page. - """ - if self.force_abs_urls: - abs_url = '%s/%s' % (self.base_path.rstrip('/'), utils.path_to_url(url.lstrip('/'))) - return abs_url - - suffix = '/' if (url.endswith('/') and len(url) > 1) else '' - # Workaround for bug on `os.path.relpath()` in Python 2.6 - if self.base_path == '/': - if url == '/': - # Workaround for static assets - return '.' - return url.lstrip('/') - # Under Python 2.6, relative_path adds an extra '/' at the end. - relative_path = os.path.relpath(url, start=self.base_path) - relative_path = relative_path.rstrip('/') + suffix - - return utils.path_to_url(relative_path) - - -class FileContext(object): - """ - The FileContext is used to ensure that we can generate the appropriate - full path for other pages given their relative path from a particular page. - - This is used when we have relative hyperlinks in the documentation, so that - we can ensure that they point to markdown documents that actually exist - in the `pages` config. - """ - def __init__(self): - self.current_file = None - self.base_path = '' - - def set_current_path(self, current_path): - self.current_file = current_path - self.base_path = os.path.dirname(current_path) - - def make_absolute(self, path): - """ - Given a relative file path return it as a POSIX-style - absolute filepath, given the context of the current page. - """ - return os.path.normpath(os.path.join(self.base_path, path)) - - -class Page(object): - def __init__(self, title, path, url_context, config): - - self._title = title - self.abs_url = utils.get_url_path(path, config['use_directory_urls']) - self.active = False - self.url_context = url_context - - # Support SOURCE_DATE_EPOCH environment variable for "reproducible" builds. - # See https://reproducible-builds.org/specs/source-date-epoch/ - if 'SOURCE_DATE_EPOCH' in os.environ: - self.update_date = datetime.datetime.utcfromtimestamp( - int(os.environ['SOURCE_DATE_EPOCH']) - ).strftime("%Y-%m-%d") - else: - self.update_date = datetime.datetime.now().strftime("%Y-%m-%d") - - # Relative and absolute paths to the input markdown file and output html file. - self.input_path = path - self.output_path = utils.get_html_path(path) - self.abs_input_path = os.path.join(config['docs_dir'], self.input_path) - self.abs_output_path = os.path.join(config['site_dir'], self.output_path) - - self.canonical_url = None - if config['site_url']: - self._set_canonical_url(config['site_url']) - - self.edit_url = None - if config['repo_url'] and config['edit_uri']: - self._set_edit_url(config['repo_url'], config['edit_uri']) - - # Placeholders to be filled in later in the build - # process when we have access to the config. - self.markdown = '' - self.meta = {} - self.content = None - self.toc = None - - self.previous_page = None - self.next_page = None - self.ancestors = [] - - def __eq__(self, other): - - def sub_dict(d): - return dict((key, value) for key, value in d.items() - if key in ['title', 'input_path', 'abs_url']) - - return (isinstance(other, self.__class__) - and sub_dict(self.__dict__) == sub_dict(other.__dict__)) - - def __ne__(self, other): - return not self.__eq__(other) - - def __str__(self): - return self.indent_print() - - def __repr__(self): - return "nav.Page(title='{0}', input_path='{1}', url='{2}')".format( - self.title, self.input_path, self.abs_url) - - @property - def title(self): - """ - Get the title for a Markdown document - Check these in order and return the first that has a valid title: - - self._title which is populated from the mkdocs.yml - - self.meta['title'] which comes from the page metadata - - self.markdown - look for the first H1 - - self.input_path - create a title based on the filename - """ - if self._title is not None: - return self._title - elif 'title' in self.meta: - return self.meta['title'] - - title = utils.get_markdown_title(self.markdown) - - if title is not None: - return title - - return _filename_to_title(self.input_path.split(os.path.sep)[-1]) - - @property - def url(self): - return self.url_context.make_relative(self.abs_url) - - @property - def is_homepage(self): - return utils.is_homepage(self.input_path) - - @property - def is_top_level(self): - return len(self.ancestors) == 0 - - def read_source(self, config): - source = config['plugins'].run_event( - 'page_read_source', None, config=config, page=self) - if source is None: - try: - with io.open(self.abs_input_path, 'r', encoding='utf-8-sig') as f: - source = f.read() - except IOError: - log.error('File not found: %s', self.abs_input_path) - raise - - self.markdown, self.meta = meta.get_data(source) - - def _set_canonical_url(self, base): - if not base.endswith('/'): - base += '/' - self.canonical_url = utils.urljoin(base, self.abs_url.lstrip('/')) - - def _set_edit_url(self, repo_url, edit_uri): - # Normalize URL from Windows path '\\' -> '/' - input_path_url = self.input_path.replace('\\', '/') - self.edit_url = utils.urljoin(repo_url, edit_uri + input_path_url) - - def indent_print(self, depth=0): - indent = ' ' * depth - active_marker = ' [*]' if self.active else '' - title = self.title if (self.title is not None) else '[blank]' - return '%s%s - %s%s\n' % (indent, title, self.abs_url, active_marker) - - def set_active(self, active=True): - self.active = active - for ancestor in self.ancestors: - ancestor.set_active(active) - - def render(self, config, site_navigation=None): - """ - Convert the Markdown source file to HTML as per the config and - site_navigation. - - """ - - extensions = [ - RelativePathExtension(site_navigation, config['strict']) - ] + config['markdown_extensions'] - - md = markdown.Markdown( - extensions=extensions, - extension_configs=config['mdx_configs'] or {} - ) - self.content = md.convert(self.markdown) - self.toc = toc.TableOfContents(getattr(md, 'toc', '')) - - -class Header(object): - def __init__(self, title, children): - self.title, self.children = title, children - self.active = False - self.ancestors = [] - - def __str__(self): - return self.indent_print() - - @property - def is_top_level(self): - return len(self.ancestors) == 0 - - def indent_print(self, depth=0): - indent = ' ' * depth - active_marker = ' [*]' if self.active else '' - ret = '%s%s%s\n' % (indent, self.title, active_marker) - for item in self.children: - ret += item.indent_print(depth + 1) - return ret - - def set_active(self, active=True): - self.active = active - for ancestor in self.ancestors: - ancestor.set_active(active) - - -def _follow(config_line, url_context, config, header=None, title=None): - - if isinstance(config_line, utils.string_types): - path = os.path.normpath(config_line) - page = Page(title, path, url_context, config) - - if header: - page.ancestors = header.ancestors + [header, ] - header.children.append(page) - - yield page - raise StopIteration - - elif not isinstance(config_line, dict): - msg = ("Line in 'page' config is of type {0}, dict or string " - "expected. Config: {1}").format(type(config_line), config_line) - raise exceptions.ConfigurationError(msg) - - if len(config_line) > 1: - raise exceptions.ConfigurationError( - "Page configs should be in the format 'name: markdown.md'. The " - "config contains an invalid entry: {0}".format(config_line)) - elif len(config_line) == 0: - log.warning("Ignoring empty line in the pages config.") - raise StopIteration - - next_cat_or_title, subpages_or_path = next(iter(config_line.items())) - - if isinstance(subpages_or_path, utils.string_types): - path = subpages_or_path - for sub in _follow(path, url_context, config, header=header, title=next_cat_or_title): - yield sub - raise StopIteration - - elif not isinstance(subpages_or_path, list): - msg = ("Line in 'page' config is of type {0}, list or string " - "expected for sub pages. Config: {1}" - ).format(type(config_line), config_line) - raise exceptions.ConfigurationError(msg) - - next_header = Header(title=next_cat_or_title, children=[]) - if header: - next_header.ancestors = [header] - header.children.append(next_header) - yield next_header - - subpages = subpages_or_path - - for subpage in subpages: - for sub in _follow(subpage, url_context, config, next_header): - yield sub - - -def _generate_site_navigation(config, url_context): - """ - Returns a list of Page and Header instances that represent the - top level site navigation. - """ - nav_items = [] - pages = [] - - previous = None - - for config_line in config['pages']: - - for page_or_header in _follow( - config_line, url_context, config): - - if isinstance(page_or_header, Header): - - if page_or_header.is_top_level: - nav_items.append(page_or_header) - - elif isinstance(page_or_header, Page): - - if page_or_header.is_top_level: - nav_items.append(page_or_header) - - pages.append(page_or_header) - - if previous: - page_or_header.previous_page = previous - previous.next_page = page_or_header - previous = page_or_header - - if len(pages) == 0: - raise exceptions.ConfigurationError( - "No pages found in the pages config. " - "Remove it entirely to enable automatic page discovery.") - - return (nav_items, pages) diff --git a/mkdocs/plugins.py b/mkdocs/plugins.py index de4e837da5..08191b3f40 100644 --- a/mkdocs/plugins.py +++ b/mkdocs/plugins.py @@ -18,7 +18,7 @@ EVENTS = ( - 'config', 'pre_build', 'nav', 'env', 'pre_template', 'template_context', + 'config', 'pre_build', 'files', 'nav', 'env', 'pre_template', 'template_context', 'post_template', 'pre_page', 'page_read_source', 'page_markdown', 'page_content', 'page_context', 'post_page', 'post_build', 'serve' ) diff --git a/mkdocs/structure/__init__.py b/mkdocs/structure/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/mkdocs/structure/files.py b/mkdocs/structure/files.py new file mode 100644 index 0000000000..f7c8a9eee6 --- /dev/null +++ b/mkdocs/structure/files.py @@ -0,0 +1,266 @@ +# coding: utf-8 + +from __future__ import unicode_literals +import fnmatch +import os +import logging +from functools import cmp_to_key + +from mkdocs import utils + + +log = logging.getLogger(__name__) + + +class Files(object): + """ A collection of File objects. """ + def __init__(self, files): + self._files = files + self.src_paths = {file.src_path: file for file in files} + + def __iter__(self): + return iter(self._files) + + def __len__(self): + return len(self._files) + + def __contains__(self, path): + return path in self.src_paths + + def get_file_from_path(self, path): + """ Return a File instance with File.src_path equal to path. """ + return self.src_paths.get(os.path.normpath(path)) + + def append(self, file): + """ Append file to Files collection. """ + self._files.append(file) + self.src_paths[file.src_path] = file + + def copy_static_files(self, dirty=False): + """ Copy static files from source to destination. """ + for file in self: + if not file.is_documentation_page(): + file.copy_file(dirty) + + def documentation_pages(self): + """ Return iterable of all Markdown page file objects. """ + return [file for file in self if file.is_documentation_page()] + + def static_pages(self): + """ Return iterable of all static page file objects. """ + return [file for file in self if file.is_static_page()] + + def media_files(self): + """ Return iterable of all file objects which are not documentation or static pages. """ + return [file for file in self if file.is_media_file()] + + def javascript_files(self): + """ Return iterable of all javascript file objects. """ + return [file for file in self if file.is_javascript()] + + def css_files(self): + """ Return iterable of all CSS file objects. """ + return [file for file in self if file.is_css()] + + def add_files_from_theme(self, env, config): + """ Retrieve static files from Jinja environment and add to collection. """ + def filter(name): + patterns = ['.*', '*.py', '*.pyc', '*.html', 'mkdocs_theme.yml'] + patterns.extend(config['theme'].static_templates) + for pattern in patterns: + if fnmatch.fnmatch(name, pattern): + return False + return True + for path in env.list_templates(filter_func=filter): + for dir in config['theme'].dirs: + # Find the first theme dir which contains path + if os.path.isfile(os.path.join(dir, path)): + self.append(File(path, dir, config['site_dir'], config['use_directory_urls'])) + break + + +class File(object): + """ + A MkDocs File object. + + Points to the source and destination locations of a file. + + The `path` argument must be a path that exists relative to `src_dir`. + + The `src_dir` and `dest_dir` must be absolute paths on the local file system. + + The `use_directory_urls` argument controls how destination paths are generated. If `False`, a Markdown file is + mapped to an HTML file of the same name (the file extension is changed to `.html`). If True, a Markdown file is + mapped to an HTML index file (`index.html`) nested in a directory using the "name" of the file in `path`. The + `use_directory_urls` argument has no effect on non-Markdown files. + + File objects have the following properties, which are Unicode strings: + + File.src_path + The pure path of the source file relative to the source directory. + + File.abs_src_path + The absolute concrete path of the source file. + + File.dest_path + The pure path of the destination file relative to the destination directory. + + File.abs_dest_path + The absolute concrete path of the destination file. + + File.url + The url of the destination file relative to the destination directory as a string. + """ + def __init__(self, path, src_dir, dest_dir, use_directory_urls): + self.page = None + self.src_path = os.path.normpath(path) + self.abs_src_path = os.path.normpath(os.path.join(src_dir, self.src_path)) + self.name = self._get_stem() + self.dest_path = self._get_dest_path(use_directory_urls) + self.abs_dest_path = os.path.normpath(os.path.join(dest_dir, self.dest_path)) + self.url = self._get_url(use_directory_urls) + + def __eq__(self, other): + + def sub_dict(d): + return dict((key, value) for key, value in d.items() if key in ['src_path', 'abs_src_path', 'url']) + + return (isinstance(other, self.__class__) and sub_dict(self.__dict__) == sub_dict(other.__dict__)) + + def __ne__(self, other): + return not self.__eq__(other) + + def _get_stem(self): + """ Return the name of the file without it's extension. """ + filename = os.path.basename(self.src_path) + stem, ext = os.path.splitext(filename) + return 'index' if stem in ('index', 'README') else stem + + def _get_dest_path(self, use_directory_urls): + """ Return destination path based on source path. """ + if self.is_documentation_page(): + if use_directory_urls: + parent, filename = os.path.split(self.src_path) + if self.name == 'index': + # index.md or README.md => index.html + return os.path.join(parent, 'index.html') + else: + # foo.md => foo/index.html + return os.path.join(parent, self.name, 'index.html') + else: + # foo.md => foo.html + root, ext = os.path.splitext(self.src_path) + return root + '.html' + return self.src_path + + def _get_url(self, use_directory_urls): + """ Return url based in destination path. """ + url = self.dest_path.replace(os.path.sep, '/') + dirname, filename = os.path.split(url) + if use_directory_urls and filename == 'index.html': + if dirname == '': + url = '.' + else: + url = dirname + '/' + return url + + def url_relative_to(self, other): + """ Return url for file relative to other file. """ + return utils.get_relative_url(self.url, other.url if isinstance(other, File) else other) + + def copy_file(self, dirty=False): + """ Copy source file to destination, ensuring parent directories exist. """ + if dirty and not self.is_modified(): + log.debug("Skip copying unmodified file: '{}'".format(self.src_path)) + else: + log.debug("Copying media file: '{}'".format(self.src_path)) + utils.copy_file(self.abs_src_path, self.abs_dest_path) + + def is_modified(self): + if os.path.isfile(self.abs_dest_path): + return os.path.getmtime(self.abs_dest_path) < os.path.getmtime(self.abs_src_path) + return True + + def is_documentation_page(self): + """ Return True if file is a Markdown page. """ + return os.path.splitext(self.src_path)[1] in utils.markdown_extensions + + def is_static_page(self): + """ Return True if file is a static page (html, xml, json). """ + return os.path.splitext(self.src_path)[1] in ( + '.html', + '.htm', + '.xml', + '.json', + ) + + def is_media_file(self): + """ Return True if file is not a documentation or static page. """ + return not (self.is_documentation_page() or self.is_static_page()) + + def is_javascript(self): + """ Return True if file is a JavaScript file. """ + return os.path.splitext(self.src_path)[1] in ( + '.js', + '.javascript', + ) + + def is_css(self): + """ Return True if file is a CSS file. """ + return os.path.splitext(self.src_path)[1] in ( + '.css', + ) + + +def get_files(config): + """ Walk the `docs_dir` and return a Files collection. """ + files = [] + exclude = ['.*', '/templates'] + + for source_dir, dirnames, filenames in os.walk(config['docs_dir'], followlinks=True): + relative_dir = os.path.relpath(source_dir, config['docs_dir']) + + for dirname in list(dirnames): + path = os.path.normpath(os.path.join(relative_dir, dirname)) + # Skip any excluded directories + if _filter_paths(basename=dirname, path=path, is_dir=True, exclude=exclude): + dirnames.remove(dirname) + dirnames.sort() + + for filename in _sort_files(filenames): + path = os.path.normpath(os.path.join(relative_dir, filename)) + # Skip any excluded files + if _filter_paths(basename=filename, path=path, is_dir=False, exclude=exclude): + continue + files.append(File(path, config['docs_dir'], config['site_dir'], config['use_directory_urls'])) + + return Files(files) + + +def _sort_files(filenames): + """ Always sort `index` as first filename in list. """ + + def compare(x, y): + if x == y: + return 0 + if os.path.splitext(y)[0] == 'index': + return 1 + if os.path.splitext(x)[0] == 'index' or x < y: + return -1 + return 1 + + return sorted(filenames, key=cmp_to_key(compare)) + + +def _filter_paths(basename, path, is_dir, exclude): + """ .gitignore style file filtering. """ + for item in exclude: + # Items ending in '/' apply only to directories. + if item.endswith('/') and not is_dir: + continue + # Items starting with '/' apply to the whole path. + # In any other cases just the basename is used. + match = path if item.startswith('/') else basename + if fnmatch.fnmatch(match, item.strip('/')): + return True + return False diff --git a/mkdocs/structure/nav.py b/mkdocs/structure/nav.py new file mode 100644 index 0000000000..782f51a6c0 --- /dev/null +++ b/mkdocs/structure/nav.py @@ -0,0 +1,182 @@ +# coding: utf-8 + +from __future__ import unicode_literals +import logging + +from mkdocs.structure.pages import Page +from mkdocs.utils import string_types, nest_paths + +log = logging.getLogger(__name__) + + +class Navigation(object): + def __init__(self, items, pages): + self.items = items # Nested List with full navigation of Sections, Pages, and Links. + self.pages = pages # Flat List of subset of Pages in nav, in order. + + self.homepage = None + for page in pages: + if page.is_homepage: + self.homepage = page + break + + def __repr__(self): + return '\n'.join([item._indent_print() for item in self]) + + def __iter__(self): + return iter(self.items) + + def __len__(self): + return len(self.items) + + +class Section(object): + def __init__(self, title, children): + self.title = title + self.children = children + + self.parent = None + self.active = False + + self.is_section = True + self.is_page = False + self.is_link = False + + def __repr__(self): + return "Section(title='{0}')".format(self.title) + + def _get_active(self): + """ Return active status of section. """ + return self.__active + + def _set_active(self, value): + """ Set active status of section and ancestors. """ + self.__active = bool(value) + if self.parent is not None: + self.parent.active = bool(value) + + active = property(_get_active, _set_active) + + @property + def ancestors(self): + if self.parent is None: + return [] + return [self.parent] + self.parent.ancestors + + def _indent_print(self, depth=0): + ret = ['{}{}'.format(' ' * depth, repr(self))] + for item in self.children: + ret.append(item._indent_print(depth + 1)) + return '\n'.join(ret) + + +class Link(object): + def __init__(self, title, url): + self.title = title + self.url = url + self.parent = None + + # These should never change but are included for consistency with sections and pages. + self.children = None + self.active = False + self.is_section = False + self.is_page = False + self.is_link = True + + def __repr__(self): + title = "'{}'".format(self.title) if (self.title is not None) else '[blank]' + return "Link(title={}, url='{}')".format(title, self.url) + + @property + def ancestors(self): + if self.parent is None: + return [] + return [self.parent] + self.parent.ancestors + + def _indent_print(self, depth=0): + return '{}{}'.format(' ' * depth, repr(self)) + + +def get_navigation(files, config): + """ Build site navigation from config and files.""" + nav_config = config['nav'] or nest_paths(f.src_path for f in files.documentation_pages()) + items = _data_to_navigation(nav_config, files, config) + if not isinstance(items, list): + items = [items] + + # Get only the pages from the navigation, ignoring any sections and links. + pages = _get_by_type(items, Page) + + # Include next, previous and parent links. + _add_previous_and_next_links(pages) + _add_parent_links(items) + + missing_from_config = [file for file in files.documentation_pages() if file.page is None] + if missing_from_config: + log.info( + 'The following pages exist in the docs directory, but are not ' + 'included in the "nav" configuration:\n - {}'.format( + '\n - '.join([file.src_path for file in missing_from_config])) + ) + # Any documentation files not found in the nav should still have an associated page. + # However, these page objects are only accessable from File instances as `file.page`. + for file in missing_from_config: + Page(None, file, config) + + links = _get_by_type(items, Link) + if links: + # Assume all links are external. + # TODO: warn or error on internal links? + log.info( + 'The following paths are included in the "nav" configuration, ' + 'but do not exist in the docs directory:\n - {}'.format( + '\n - '.join([link.url for link in links])) + ) + return Navigation(items, pages) + + +def _data_to_navigation(data, files, config): + if isinstance(data, dict): + return [ + _data_to_navigation((key, value), files, config) + if isinstance(value, string_types) else + Section(title=key, children=_data_to_navigation(value, files, config)) + for key, value in data.items() + ] + elif isinstance(data, list): + return [ + _data_to_navigation(item, files, config)[0] + if isinstance(item, dict) and len(item) == 1 else + _data_to_navigation(item, files, config) + for item in data + ] + title, path = data if isinstance(data, tuple) else (None, data) + file = files.get_file_from_path(path) + if file: + return Page(title, file, config) + return Link(title, path) + + +def _get_by_type(nav, T): + ret = [] + for item in nav: + if isinstance(item, T): + ret.append(item) + elif item.children: + ret.extend(_get_by_type(item.children, T)) + return ret + + +def _add_parent_links(nav): + for item in nav: + if item.is_section: + for child in item.children: + child.parent = item + _add_parent_links(item.children) + + +def _add_previous_and_next_links(pages): + bookended = [None] + pages + [None] + zipped = zip(bookended[:-2], bookended[1:-1], bookended[2:]) + for page0, page1, page2 in zipped: + page1.previous_page, page1.next_page = page0, page2 diff --git a/mkdocs/structure/pages.py b/mkdocs/structure/pages.py new file mode 100644 index 0000000000..77086ed71d --- /dev/null +++ b/mkdocs/structure/pages.py @@ -0,0 +1,266 @@ +# coding: utf-8 + +from __future__ import unicode_literals + +import os +import io +import datetime +import logging + +import markdown +from markdown.extensions import Extension +from markdown.treeprocessors import Treeprocessor +from markdown.util import AMP_SUBSTITUTE + +from mkdocs.structure.toc import get_toc +from mkdocs.utils import meta, urlparse, urlunparse, urljoin, get_markdown_title +from mkdocs.exceptions import MarkdownNotFound + +log = logging.getLogger(__name__) + + +@meta.transformer() +def default(value): + """ By default, return all meta values as strings. """ + return ' '.join(value) + + +class Page(object): + def __init__(self, title, file, config): + file.page = self + self.file = file + self.title = title + + # Navigation attributes + self.parent = None + self.children = None + self.previous_page = None + self.next_page = None + self.active = False + + self.is_section = False + self.is_page = True + self.is_link = False + + # Support SOURCE_DATE_EPOCH environment variable for "reproducible" builds. + # See https://reproducible-builds.org/specs/source-date-epoch/ + if 'SOURCE_DATE_EPOCH' in os.environ: + self.update_date = datetime.datetime.utcfromtimestamp( + int(os.environ['SOURCE_DATE_EPOCH']) + ).strftime("%Y-%m-%d") + else: + self.update_date = datetime.datetime.now().strftime("%Y-%m-%d") + + self._set_canonical_url(config.get('site_url', None)) + self._set_edit_url(config.get('repo_url', None), config.get('edit_uri', None)) + + # Placeholders to be filled in later in the build process. + self.markdown = None + self.content = None + self.toc = [] + self.meta = {} + + def __eq__(self, other): + + def sub_dict(d): + return dict((key, value) for key, value in d.items() if key in ['title', 'file']) + + return (isinstance(other, self.__class__) and sub_dict(self.__dict__) == sub_dict(other.__dict__)) + + def __ne__(self, other): + return not self.__eq__(other) + + def __repr__(self): + title = "'{}'".format(self.title) if (self.title is not None) else '[blank]' + return "Page(title={}, url='{}')".format(title, self.abs_url or self.file.url) + + def _indent_print(self, depth=0): + return '{}{}'.format(' ' * depth, repr(self)) + + def _get_active(self): + """ Return active status of page. """ + return self.__active + + def _set_active(self, value): + """ Set active status of page and ancestors. """ + self.__active = bool(value) + if self.parent is not None: + self.parent.active = bool(value) + + active = property(_get_active, _set_active) + + @property + def is_index(self): + return self.file.name == 'index' + + @property + def is_top_level(self): + return self.parent is None + + @property + def is_homepage(self): + return self.is_top_level and self.is_index + + @property + def url(self): + return '' if self.file.url == '.' else self.file.url + + @property + def ancestors(self): + if self.parent is None: + return [] + return [self.parent] + self.parent.ancestors + + def _set_canonical_url(self, base): + if base: + if not base.endswith('/'): + base += '/' + self.canonical_url = urljoin(base, self.url) + self.abs_url = urlparse(self.canonical_url).path + else: + self.canonical_url = None + self.abs_url = None + + def _set_edit_url(self, repo_url, edit_uri): + if repo_url and edit_uri: + src_path = self.file.src_path.replace('\\', '/') + self.edit_url = urljoin(repo_url, edit_uri + src_path) + else: + self.edit_url = None + + def read_source(self, config): + source = config['plugins'].run_event('page_read_source', None, config=config, page=self) + if source is None: + try: + with io.open(self.file.abs_src_path, 'r', encoding='utf-8-sig', errors='strict') as f: + source = f.read() + except IOError: + log.error('File not found: {}'.format(self.file.src_path)) + raise + except ValueError: + log.error('Encoding error reading file: {}'.format(self.file.src_path)) + raise + + self.markdown, self.meta = meta.get_data(source) + self._set_title() + + def _set_title(self): + """ + Set the title for a Markdown document. + + Check these in order and use the first that returns a valid title: + - value provided on init (passed in from config) + - value of metadata 'title' + - content of the first H1 in Markdown content + - convert filename to title + """ + if self.title is not None: + return + + if 'title' in self.meta: + self.title = self.meta['title'] + return + + title = get_markdown_title(self.markdown) + + if title is None: + if self.is_homepage: + title = 'Home' + else: + title = self.file.name.replace('-', ' ').replace('_', ' ') + # Capitalize if the filename was all lowercase, otherwise leave it as-is. + if title.lower() == title: + title = title.capitalize() + + self.title = title + + def render(self, config, files): + """ + Convert the Markdown source file to HTML as per the config. + """ + + extensions = [ + _RelativePathExtension(self.file, files, config['strict']) + ] + config['markdown_extensions'] + + md = markdown.Markdown( + extensions=extensions, + extension_configs=config['mdx_configs'] or {} + ) + self.content = md.convert(self.markdown) + self.toc = get_toc(getattr(md, 'toc', '')) + + +class _RelativePathTreeprocessor(Treeprocessor): + def __init__(self, file, files, strict): + self.file = file + self.files = files + self.strict = strict + + def run(self, root): + """ + Update urls on anchors and images to make them relative + + Iterates through the full document tree looking for specific + tags and then makes them relative based on the site navigation + """ + for element in root.iter(): + if element.tag == 'a': + key = 'href' + elif element.tag == 'img': + key = 'src' + else: + continue + + url = element.get(key) + new_url = self.path_to_url(url) + element.set(key, new_url) + + return root + + def path_to_url(self, url): + scheme, netloc, path, params, query, fragment = urlparse(url) + + if scheme or netloc or not path or AMP_SUBSTITUTE in url or '.' not in os.path.split(path)[-1]: + # Ignore URLs unless they are a relative link to a source file. + # AMP_SUBSTITUTE is used internally by Markdown only for email. + # No '.' in the last part of a path indicates path does not point to a file. + return url + + # Determine the filepath of the target. + target_path = os.path.join(os.path.dirname(self.file.src_path), path) + target_path = os.path.normpath(target_path).lstrip(os.sep) + + # Validate that the target exists in files collection. + if target_path not in self.files: + msg = ( + "Documentation file '{}' contains a link to '{}' which does not exist " + "in the documentation directory.".format(self.file.src_path, target_path) + ) + # In strict mode raise an error at this point. + if self.strict: + raise MarkdownNotFound(msg) + # Otherwise, when strict mode isn't enabled, log a warning + # to the user and leave the URL as it is. + log.warning(msg) + return url + target_file = self.files.get_file_from_path(target_path) + path = target_file.url_relative_to(self.file) + components = (scheme, netloc, path, params, query, fragment) + return urlunparse(components) + + +class _RelativePathExtension(Extension): + """ + The Extension class is what we pass to markdown, it then + registers the Treeprocessor. + """ + + def __init__(self, file, files, strict): + self.file = file + self.files = files + self.strict = strict + + def extendMarkdown(self, md, md_globals): + relpath = _RelativePathTreeprocessor(self.file, self.files, self.strict) + md.treeprocessors.add("relpath", relpath, "_end") diff --git a/mkdocs/structure/toc.py b/mkdocs/structure/toc.py new file mode 100644 index 0000000000..2cd2cf6409 --- /dev/null +++ b/mkdocs/structure/toc.py @@ -0,0 +1,131 @@ +# coding: utf-8 +""" +Deals with generating the per-page table of contents. + +For the sake of simplicity we use an existing markdown extension to generate +an HTML table of contents, and then parse that into the underlying data. +""" +from __future__ import unicode_literals + +try: # pragma: no cover + from html.parser import HTMLParser # noqa +except ImportError: # pragma: no cover + from HTMLParser import HTMLParser # noqa + + +def get_toc(toc_html): + items = _parse_html_table_of_contents(toc_html) + return TableOfContents(items) + + +class TableOfContents(object): + """ + Represents the table of contents for a given page. + """ + def __init__(self, items): + self.items = items + + def __iter__(self): + return iter(self.items) + + def __len__(self): + return len(self.items) + + def __str__(self): + return ''.join([str(item) for item in self]) + + +class AnchorLink(object): + """ + A single entry in the table of contents. + """ + def __init__(self, title, url): + self.title, self.url = title, url + self.children = [] + + def __str__(self): + return self.indent_print() + + def indent_print(self, depth=0): + indent = ' ' * depth + ret = '%s%s - %s\n' % (indent, self.title, self.url) + for item in self.children: + ret += item.indent_print(depth + 1) + return ret + + +class _TOCParser(HTMLParser): + def __init__(self): + HTMLParser.__init__(self) + self.links = [] + + self.in_anchor = False + self.attrs = None + self.title = '' + + # Prior to Python3.4 no convert_charrefs keyword existed. + # However, in Python3.5 the default was changed to True. + # We need the False behavior in all versions but can only + # set it if it exists. + if hasattr(self, 'convert_charrefs'): # pragma: no cover + self.convert_charrefs = False + + def handle_starttag(self, tag, attrs): + if not self.in_anchor: + if tag == 'a': + self.in_anchor = True + self.attrs = dict(attrs) + + def handle_endtag(self, tag): + if tag == 'a': + self.in_anchor = False + + def handle_data(self, data): + if self.in_anchor: + self.title += data + + def handle_charref(self, ref): + self.handle_entityref("#" + ref) + + def handle_entityref(self, ref): + self.handle_data("&%s;" % ref) + + +def _parse_html_table_of_contents(html): + """ + Given a table of contents string that has been automatically generated by + the markdown library, parse it into a tree of AnchorLink instances. + + Returns a list of all the parent AnchorLink instances. + """ + lines = html.splitlines()[2:-2] + parents = [] + ret = [] + for line in lines: + parser = _TOCParser() + parser.feed(line) + if parser.title: + try: + href = parser.attrs['href'] + except KeyError: + continue + title = parser.title + nav = AnchorLink(title, href) + # Add the item to its parent if required. If it is a topmost + # item then instead append it to our return value. + if parents: + parents[-1].children.append(nav) + else: + ret.append(nav) + # If this item has children, store it as the current parent + if line.endswith('
      '): + parents.append(nav) + elif line.startswith('
    '): + if parents: + parents.pop() + + # For the table of contents, always mark the first element as active + if ret: + ret[0].active = True + + return ret diff --git a/mkdocs/tests/base.py b/mkdocs/tests/base.py index a17fc2e072..f975c42a30 100644 --- a/mkdocs/tests/base.py +++ b/mkdocs/tests/base.py @@ -5,9 +5,15 @@ import logging import collections import unittest +from functools import wraps + +try: + # py>=3.2 + from tempfile import TemporaryDirectory +except ImportError: + from backports.tempfile import TemporaryDirectory -from mkdocs import toc from mkdocs import config from mkdocs import utils @@ -16,11 +22,11 @@ def dedent(text): return textwrap.dedent(text).strip() -def markdown_to_toc(markdown_source): +def get_markdown_toc(markdown_source): + """ Return TOC generated by Markdown parser from Markdown source text. """ md = markdown.Markdown(extensions=['toc']) md.convert(markdown_source) - toc_output = md.toc - return toc.TableOfContents(toc_output) + return md.toc def load_config(**cfg): @@ -44,6 +50,95 @@ def load_config(**cfg): return conf +def tempdir(files=None, **kw): + """ + A decorator for building a temporary directory with prepopulated files. + + The temproary directory and files are created just before the wrapped function is called and are destroyed + imediately after the wrapped function returns. + + The `files` keyword should be a dict of file paths as keys and strings of file content as values. + If `files` is a list, then each item is assumed to be a path of an empty file. All other + keywords are passed to `tempfile.TemporaryDirectory` to create the parent directory. + + In the following example, two files are created in the temporary directory and then are destroyed when + the function exits: + + @tempdir(files={ + 'foo.txt': 'foo content', + 'bar.txt': 'bar content' + }) + def example(self, tdir): + assert os.path.isfile(os.path.join(tdir, 'foo.txt')) + pth = os.path.join(tdir, 'bar.txt') + assert os.path.isfile(pth) + with io.open(pth, 'r', encoding='utf-8') as f: + assert f.read() == 'bar content' + """ + files = {f: '' for f in files} if isinstance(files, (list, tuple)) else files or {} + + if 'prefix' not in kw: + kw['prefix'] = 'mkdocs_test-' + + def decorator(fn): + @wraps(fn) + def wrapper(self, *args): + with TemporaryDirectory(**kw) as td: + for path, content in files.items(): + pth = os.path.join(td, path) + utils.write_file(content.encode(encoding='utf-8'), pth) + return fn(self, td, *args) + return wrapper + return decorator + + +class PathAssertionMixin(object): + """ + Assertion methods for testing paths. + + Each method accepts one or more strings, which are first joined using os.path.join. + """ + + def assertPathsEqual(self, a, b, msg=None): + self.assertEqual(a.replace('\\', '/'), b.replace('\\', '/')) + + def assertPathExists(self, *parts): + path = os.path.join(*parts) + if not os.path.exists(path): + msg = self._formatMessage(None, "The path '{}' does not exist".format(path)) + raise self.failureException(msg) + + def assertPathNotExists(self, *parts): + path = os.path.join(*parts) + if os.path.exists(path): + msg = self._formatMessage(None, "The path '{}' does exist".format(path)) + raise self.failureException(msg) + + def assertPathIsFile(self, *parts): + path = os.path.join(*parts) + if not os.path.isfile(path): + msg = self._formatMessage(None, "The path '{}' is not a file that exists".format(path)) + raise self.failureException(msg) + + def assertPathNotFile(self, *parts): + path = os.path.join(*parts) + if os.path.isfile(path): + msg = self._formatMessage(None, "The path '{}' is a file that exists".format(path)) + raise self.failureException(msg) + + def assertPathIsDir(self, *parts): + path = os.path.join(*parts) + if not os.path.isdir(path): + msg = self._formatMessage(None, "The path '{}' is not a directory that exists".format(path)) + raise self.failureException(msg) + + def assertPathNotDir(self, *parts): + path = os.path.join(*parts) + if os.path.isfile(path): + msg = self._formatMessage(None, "The path '{}' is a directory that exists".format(path)) + raise self.failureException(msg) + + # Backport unittest.TestCase.assertLogs for Python 2.7 # see https://github.com/python/cpython/blob/3.6/Lib/unittest/case.py diff --git a/mkdocs/tests/build_tests.py b/mkdocs/tests/build_tests.py index 32b86206c3..a85693bcbd 100644 --- a/mkdocs/tests/build_tests.py +++ b/mkdocs/tests/build_tests.py @@ -2,482 +2,472 @@ # coding: utf-8 from __future__ import unicode_literals -import os -import unittest import mock -import io -try: - from itertools import izip as zip -except ImportError: - # In Py3 use builtin zip function - pass - -try: - # py>=3.2 - from tempfile import TemporaryDirectory -except ImportError: - from backports.tempfile import TemporaryDirectory - -from mkdocs import nav +from mkdocs.structure.pages import Page +from mkdocs.structure.files import File, Files +from mkdocs.structure.nav import get_navigation from mkdocs.commands import build -from mkdocs.exceptions import MarkdownNotFound -from mkdocs.tests.base import dedent, load_config +from mkdocs.tests.base import load_config, LogTestCase, tempdir, PathAssertionMixin from mkdocs.utils import meta -def build_page(title, path, config, md_src=None): +def build_page(title, path, config, md_src=''): """ Helper which returns a Page object. """ - sitenav = nav.SiteNavigation(config) - page = nav.Page(title, path, sitenav.url_context, config) - if md_src: - # Fake page.read_source() - page.markdown, page.meta = meta.get_data(md_src) - return page, sitenav - - -class BuildTests(unittest.TestCase): - - def test_empty_document(self): - config = load_config(pages=[{'Home': 'index.md'}]) - page, nav = build_page(None, 'index.md', config) - page.render(config, nav) - - self.assertEqual(page.content, '') - self.assertEqual(len(list(page.toc)), 0) - self.assertEqual(page.meta, {}) - self.assertEqual(page.title, 'Home') - - def test_convert_markdown(self): - """ - Ensure that basic Markdown -> HTML and TOC works. - """ - md_text = dedent(""" - title: custom title - - # Heading 1 - - This is some text. - - # Heading 2 - - And some more text. - """) - - config = load_config(pages=[{'Home': 'index.md'}]) - page, nav = build_page(None, 'index.md', config, md_text) - page.render(config, nav) - - expected_html = dedent(""" -

    Heading 1

    -

    This is some text.

    -

    Heading 2

    -

    And some more text.

    - """) - - expected_toc = dedent(""" - Heading 1 - #heading-1 - Heading 2 - #heading-2 - """) - - expected_meta = {'title': 'custom title'} - - self.assertEqual(page.content.strip(), expected_html) - self.assertEqual(str(page.toc).strip(), expected_toc) - self.assertEqual(page.meta, expected_meta) - self.assertEqual(page.title, 'custom title') - - def test_convert_internal_link(self): - md_text = 'An [internal link](internal.md) to another document.' - expected = '

    An internal link to another document.

    ' - config = load_config(pages=['index.md', 'internal.md']) - page, nav = build_page(None, 'index.md', config, md_text) - page.render(config, nav) - self.assertEqual(page.content.strip(), expected.strip()) - - def test_convert_multiple_internal_links(self): - md_text = '[First link](first.md) [second link](second.md).' - expected = '

    First link second link.

    ' - config = load_config(pages=['index.md', 'first.md', 'second.md']) - page, nav = build_page(None, 'index.md', config, md_text) - page.render(config, nav) - self.assertEqual(page.content.strip(), expected.strip()) - - def test_convert_internal_link_differing_directory(self): - md_text = 'An [internal link](../internal.md) to another document.' - expected = '

    An internal link to another document.

    ' - config = load_config(pages=['foo/bar.md', 'internal.md']) - page, nav = build_page(None, 'foo/bar.md', config, md_text) - page.render(config) - self.assertEqual(page.content.strip(), expected.strip()) - - def test_convert_internal_link_with_anchor(self): - md_text = 'An [internal link](internal.md#section1.1) to another document.' - expected = '

    An internal link to another document.

    ' - config = load_config(pages=['index.md', 'internal.md']) - page, nav = build_page(None, 'index.md', config, md_text) - page.render(config, nav) - self.assertEqual(page.content.strip(), expected.strip()) - - def test_convert_internal_media(self): - """Test relative image URL's are the same for different base_urls""" - pages = [ - 'index.md', - 'internal.md', - 'sub/internal.md', - ] - - config = load_config(pages=pages) - site_navigation = nav.SiteNavigation(config) + files = Files([File(path, config['docs_dir'], config['site_dir'], config['use_directory_urls'])]) + page = Page(title, list(files)[0], config) + # Fake page.read_source() + page.markdown, page.meta = meta.get_data(md_src) + return page, files - expected_results = ( - './img/initial-layout.png', - '../img/initial-layout.png', - '../img/initial-layout.png', - ) - template = '

    The initial MkDocs layout

    ' +class BuildTests(PathAssertionMixin, LogTestCase): - for (page, expected) in zip(site_navigation.walk_pages(), expected_results): - page.markdown = '![The initial MkDocs layout](img/initial-layout.png)' - page.render(config, site_navigation) - self.assertEqual(page.content, template % expected) + # Test build.get_context - def test_convert_internal_asbolute_media(self): - """Test absolute image URL's are correct for different base_urls""" - pages = [ - 'index.md', - 'internal.md', - 'sub/internal.md', + def test_context_base_url_homepage(self): + nav_cfg = [ + {'Home': 'index.md'} ] + cfg = load_config(nav=nav_cfg, use_directory_urls=False) + files = Files([ + File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + ]) + nav = get_navigation(files, cfg) + context = build.get_context(nav, files, cfg, nav.pages[0]) + self.assertEqual(context['base_url'], '.') - config = load_config(pages=pages) - site_navigation = nav.SiteNavigation(config) - - expected_results = ( - './img/initial-layout.png', - '../img/initial-layout.png', - '../../img/initial-layout.png', - ) - - template = '

    The initial MkDocs layout

    ' - - for (page, expected) in zip(site_navigation.walk_pages(), expected_results): - page.markdown = '![The initial MkDocs layout](/img/initial-layout.png)' - page.render(config, site_navigation) - self.assertEqual(page.content, template % expected) - - def test_dont_convert_code_block_urls(self): - pages = [ - 'index.md', - 'internal.md', - 'sub/internal.md', + def test_context_base_url_homepage_use_directory_urls(self): + nav_cfg = [ + {'Home': 'index.md'} ] - - config = load_config(pages=pages) - site_navigation = nav.SiteNavigation(config) - - expected = dedent(""" -

    An HTML Anchor::

    -
    <a href="index.md">My example link</a>
    -        
    - """) - - for page in site_navigation.walk_pages(): - page.markdown = 'An HTML Anchor::\n\n My example link\n' - page.render(config, site_navigation) - self.assertEqual(page.content, expected) - - def test_anchor_only_link(self): - pages = [ - 'index.md', - 'internal.md', - 'sub/internal.md', + cfg = load_config(nav=nav_cfg) + files = Files([ + File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + ]) + nav = get_navigation(files, cfg) + context = build.get_context(nav, files, cfg, nav.pages[0]) + self.assertEqual(context['base_url'], '.') + + def test_context_base_url_nested_page(self): + nav_cfg = [ + {'Home': 'index.md'}, + {'Nested': 'foo/bar.md'} ] - - config = load_config(pages=pages) - site_navigation = nav.SiteNavigation(config) - - for page in site_navigation.walk_pages(): - page.markdown = '[test](#test)' - page.render(config, site_navigation) - self.assertEqual(page.content, '

    test

    ') - - def test_ignore_external_link(self): - md_text = 'An [external link](http://example.com/external.md).' - expected = '

    An external link.

    ' - config = load_config(pages=[{'Home': 'index.md'}]) - page, nav = build_page(None, 'index.md', config, md_text) - page.render(config, nav) - self.assertEqual(page.content.strip(), expected.strip()) - - def test_not_use_directory_urls(self): - md_text = 'An [internal link](internal.md) to another document.' - expected = '

    An internal link to another document.

    ' - config = load_config(pages=['index.md', 'internal.md'], use_directory_urls=False) - page, nav = build_page(None, 'index.md', config, md_text) - page.render(config, nav) - self.assertEqual(page.content.strip(), expected.strip()) - - def test_ignore_email_links(self): - md_text = 'A and an [link](mailto:example@example.com).' - expected = ''.join([ - '

    A autolin', - 'k@example.com', - ' and an link.

    ' + cfg = load_config(nav=nav_cfg, use_directory_urls=False) + files = Files([ + File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('foo/bar.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) ]) - config = load_config(pages=[{'Home': 'index.md'}]) - page, nav = build_page(None, 'index.md', config, md_text) - page.render(config, nav) - self.assertEqual(page.content.strip(), expected.strip()) - - def test_markdown_table_extension(self): - """ - Ensure that the table extension is supported. - """ - md_text = dedent(""" - First Header | Second Header - -------------- | -------------- - Content Cell 1 | Content Cell 2 - Content Cell 3 | Content Cell 4 - """) - - expected_html = dedent(""" - - - - - - - - - - - - - - - - - -
    First HeaderSecond Header
    Content Cell 1Content Cell 2
    Content Cell 3Content Cell 4
    - """) - - config = load_config(pages=[{'Home': 'index.md'}]) - page, nav = build_page(None, 'index.md', config, md_text) - page.render(config, nav) - self.assertEqual(page.content.strip(), expected_html) - - def test_markdown_fenced_code_extension(self): - """ - Ensure that the fenced code extension is supported. - """ - md_text = dedent(""" - ``` - print 'foo' - ``` - """) - - expected_html = dedent(""" -
    print 'foo'\n
    - """) - - config = load_config(pages=[{'Home': 'index.md'}]) - page, nav = build_page(None, 'index.md', config, md_text) - page.render(config, nav) - self.assertEqual(page.content.strip(), expected_html) - - def test_markdown_custom_extension(self): - """ - Check that an extension applies when requested in the arguments to - `convert_markdown`. - """ - md_text = "foo__bar__baz" - - # Check that the plugin is not active when not requested. - expected_without_smartstrong = "

    foobarbaz

    " - config = load_config(pages=[{'Home': 'index.md'}]) - page, nav = build_page(None, 'index.md', config, md_text) - page.render(config, nav) - self.assertEqual(page.content.strip(), expected_without_smartstrong) - - # Check that the plugin is active when requested. - expected_with_smartstrong = "

    foo__bar__baz

    " - config = load_config(pages=[{'Home': 'index.md'}], markdown_extensions=['smart_strong']) - page, nav = build_page(None, 'index.md', config, md_text) - page.render(config, nav) - self.assertEqual(page.content.strip(), expected_with_smartstrong) - - def test_markdown_duplicate_custom_extension(self): - """ - Duplicated extension names should not cause problems. - """ - md_text = "foo" - config = load_config(pages=[{'Home': 'index.md'}], markdown_extensions=['toc']) - page, nav = build_page(None, 'index.md', config, md_text) - page.render(config, nav) - self.assertEqual(page.content.strip(), '

    foo

    ') - - def test_copying_media(self): - with TemporaryDirectory() as docs_dir, TemporaryDirectory() as site_dir: - # Create a non-empty markdown file, image, html file, dot file and dot directory. - f = open(os.path.join(docs_dir, 'index.md'), 'w') - f.write(dedent(""" - page_title: custom title - - # Heading 1 - - This is some text. - - # Heading 2 - - And some more text. - """)) - f.close() - open(os.path.join(docs_dir, 'img.jpg'), 'w').close() - open(os.path.join(docs_dir, 'example.html'), 'w').close() - open(os.path.join(docs_dir, '.hidden'), 'w').close() - os.mkdir(os.path.join(docs_dir, '.git')) - open(os.path.join(docs_dir, '.git/hidden'), 'w').close() - - cfg = load_config(docs_dir=docs_dir, site_dir=site_dir) - build.build(cfg) - - # Verify only the markdown (coverted to html) and the image are copied. - self.assertTrue(os.path.isfile(os.path.join(site_dir, 'index.html'))) - self.assertTrue(os.path.isfile(os.path.join(site_dir, 'img.jpg'))) - self.assertTrue(os.path.isfile(os.path.join(site_dir, 'example.html'))) - self.assertFalse(os.path.isfile(os.path.join(site_dir, '.hidden'))) - self.assertFalse(os.path.isfile(os.path.join(site_dir, '.git/hidden'))) - - def test_copy_theme_files(self): - with TemporaryDirectory() as docs_dir, TemporaryDirectory() as site_dir: - # Create a non-empty markdown file. - f = open(os.path.join(docs_dir, 'index.md'), 'w') - f.write(dedent(""" - page_title: custom title - - # Heading 1 - - This is some text. - """)) - f.close() - - cfg = load_config(docs_dir=docs_dir, site_dir=site_dir) - build.build(cfg) - - # Verify only theme media are copied, not templates or Python files. - self.assertTrue(os.path.isfile(os.path.join(site_dir, 'index.html'))) - self.assertTrue(os.path.isdir(os.path.join(site_dir, 'js'))) - self.assertTrue(os.path.isdir(os.path.join(site_dir, 'css'))) - self.assertTrue(os.path.isdir(os.path.join(site_dir, 'img'))) - self.assertFalse(os.path.isfile(os.path.join(site_dir, '__init__.py'))) - self.assertFalse(os.path.isfile(os.path.join(site_dir, '__init__.pyc'))) - self.assertFalse(os.path.isfile(os.path.join(site_dir, 'base.html'))) - self.assertFalse(os.path.isfile(os.path.join(site_dir, 'content.html'))) - self.assertFalse(os.path.isfile(os.path.join(site_dir, 'nav.html'))) - - def test_strict_mode_valid(self): - pages = [ - 'index.md', - 'internal.md', - 'sub/internal.md', + nav = get_navigation(files, cfg) + context = build.get_context(nav, files, cfg, nav.pages[1]) + self.assertEqual(context['base_url'], '..') + + def test_context_base_url_nested_page_use_directory_urls(self): + nav_cfg = [ + {'Home': 'index.md'}, + {'Nested': 'foo/bar.md'} ] - - md_text = "[test](internal.md)" - - config = load_config(pages=pages, strict=False) - page, nav = build_page(None, 'index.md', config, md_text) - page.render(config, nav) - - config = load_config(pages=pages, strict=True) - page, nav = build_page(None, 'index.md', config, md_text) - page.render(config, nav) - - def test_strict_mode_invalid(self): - pages = [ - 'index.md', - 'internal.md', - 'sub/internal.md', + cfg = load_config(nav=nav_cfg) + files = Files([ + File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('foo/bar.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + ]) + nav = get_navigation(files, cfg) + context = build.get_context(nav, files, cfg, nav.pages[1]) + self.assertEqual(context['base_url'], '../..') + + def test_context_base_url_relative_no_page(self): + cfg = load_config(use_directory_urls=False) + context = build.get_context(mock.Mock(), mock.Mock(), cfg, base_url='..') + self.assertEqual(context['base_url'], '..') + + def test_context_base_url_relative_no_page_use_directory_urls(self): + cfg = load_config() + context = build.get_context(mock.Mock(), mock.Mock(), cfg, base_url='..') + self.assertEqual(context['base_url'], '..') + + def test_context_base_url_absolute_no_page(self): + cfg = load_config(use_directory_urls=False) + context = build.get_context(mock.Mock(), mock.Mock(), cfg, base_url='/') + self.assertEqual(context['base_url'], '') + + def test_context_base_url__absolute_no_page_use_directory_urls(self): + cfg = load_config() + context = build.get_context(mock.Mock(), mock.Mock(), cfg, base_url='/') + self.assertEqual(context['base_url'], '') + + def test_context_base_url_absolute_nested_no_page(self): + cfg = load_config(use_directory_urls=False) + context = build.get_context(mock.Mock(), mock.Mock(), cfg, base_url='/foo/') + self.assertEqual(context['base_url'], '/foo') + + def test_context_base_url__absolute_nested_no_page_use_directory_urls(self): + cfg = load_config() + context = build.get_context(mock.Mock(), mock.Mock(), cfg, base_url='/foo/') + self.assertEqual(context['base_url'], '/foo') + + def test_context_extra_css_js_from_homepage(self): + nav_cfg = [ + {'Home': 'index.md'} ] + cfg = load_config( + nav=nav_cfg, + extra_css=['style.css'], + extra_javascript=['script.js'], + use_directory_urls=False + ) + files = Files([ + File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + ]) + nav = get_navigation(files, cfg) + context = build.get_context(nav, files, cfg, nav.pages[0]) + self.assertEqual(context['extra_css'], ['style.css']) + self.assertEqual(context['extra_javascript'], ['script.js']) + + def test_context_extra_css_js_from_nested_page(self): + nav_cfg = [ + {'Home': 'index.md'}, + {'Nested': 'foo/bar.md'} + ] + cfg = load_config( + nav=nav_cfg, + extra_css=['style.css'], + extra_javascript=['script.js'], + use_directory_urls=False + ) + files = Files([ + File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('foo/bar.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + ]) + nav = get_navigation(files, cfg) + context = build.get_context(nav, files, cfg, nav.pages[1]) + self.assertEqual(context['extra_css'], ['../style.css']) + self.assertEqual(context['extra_javascript'], ['../script.js']) + + def test_context_extra_css_js_from_nested_page_use_directory_urls(self): + nav_cfg = [ + {'Home': 'index.md'}, + {'Nested': 'foo/bar.md'} + ] + cfg = load_config( + nav=nav_cfg, + extra_css=['style.css'], + extra_javascript=['script.js'] + ) + files = Files([ + File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('foo/bar.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + ]) + nav = get_navigation(files, cfg) + context = build.get_context(nav, files, cfg, nav.pages[1]) + self.assertEqual(context['extra_css'], ['../../style.css']) + self.assertEqual(context['extra_javascript'], ['../../script.js']) - md_text = "[test](bad_link.md)" - - config = load_config(pages=pages, strict=False) - page, nav = build_page(None, 'index.md', config, md_text) - page.render(config, nav) + def test_context_extra_css_js_no_page(self): + cfg = load_config(extra_css=['style.css'], extra_javascript=['script.js']) + context = build.get_context(mock.Mock(), mock.Mock(), cfg, base_url='..') + self.assertEqual(context['extra_css'], ['../style.css']) + self.assertEqual(context['extra_javascript'], ['../script.js']) - config = load_config(pages=pages, strict=True) - page, nav = build_page(None, 'index.md', config, md_text) - self.assertRaises( - MarkdownNotFound, - page.render, config, nav) + def test_extra_context(self): + cfg = load_config(extra={'a': 1}) + context = build.get_context(mock.Mock(), mock.Mock(), cfg) + self.assertEqual(context['config']['extra']['a'], 1) - def test_absolute_link(self): - pages = [ - 'index.md', - 'sub/index.md', - ] + # Test build._build_theme_template + + @mock.patch('mkdocs.utils.write_file') + @mock.patch('mkdocs.commands.build._build_template', return_value='some content') + def test_build_theme_template(self, mock_build_template, mock_write_file): + cfg = load_config() + env = cfg['theme'].get_env() + build._build_theme_template('main.html', env, mock.Mock(), cfg, mock.Mock()) + mock_write_file.assert_called_once() + mock_build_template.assert_called_once() + + @mock.patch('mkdocs.utils.write_file') + @mock.patch('mkdocs.commands.build._build_template', return_value='some content') + @mock.patch('gzip.open') + def test_build_sitemap_template(self, mock_gzip_open, mock_build_template, mock_write_file): + cfg = load_config() + env = cfg['theme'].get_env() + build._build_theme_template('sitemap.xml', env, mock.Mock(), cfg, mock.Mock()) + mock_write_file.assert_called_once() + mock_build_template.assert_called_once() + mock_gzip_open.assert_called_once() + + @mock.patch('mkdocs.utils.write_file') + @mock.patch('mkdocs.commands.build._build_template', return_value='') + def test_skip_missing_theme_template(self, mock_build_template, mock_write_file): + cfg = load_config() + env = cfg['theme'].get_env() + with self.assertLogs('mkdocs', level='WARN') as cm: + build._build_theme_template('missing.html', env, mock.Mock(), cfg, mock.Mock()) + self.assertEqual( + cm.output, + ["WARNING:mkdocs.commands.build:Template skipped: 'missing.html' not found in theme directories."] + ) + mock_write_file.assert_not_called() + mock_build_template.assert_not_called() + + @mock.patch('mkdocs.utils.write_file') + @mock.patch('mkdocs.commands.build._build_template', return_value='') + def test_skip_theme_template_empty_output(self, mock_build_template, mock_write_file): + cfg = load_config() + env = cfg['theme'].get_env() + with self.assertLogs('mkdocs', level='INFO') as cm: + build._build_theme_template('main.html', env, mock.Mock(), cfg, mock.Mock()) + self.assertEqual( + cm.output, + ["INFO:mkdocs.commands.build:Template skipped: 'main.html' generated empty output."] + ) + mock_write_file.assert_not_called() + mock_build_template.assert_called_once() - md_text = "[test 1](/index.md) [test 2](/sub/index.md)" - config = load_config(pages=pages, strict=True) - page, nav = build_page(None, 'index.md', config, md_text) - page.render(config, nav) - - def test_extension_config(self): - """ - Test that a dictionary of 'markdown_extensions' is recognized as - both a list of extensions and a dictionary of extnesion configs. - """ - md_text = dedent(""" - # A Header - """) - - expected_html = dedent(""" -

    A Header

    - """) - - config = load_config(pages=[{'Home': 'index.md'}], markdown_extensions=[{'toc': {'permalink': True}}]) - page, nav = build_page(None, 'index.md', config, md_text) - page.render(config, nav) - self.assertEqual(page.content.strip(), expected_html) + # Test build._build_extra_template - def test_extra_context(self): + @mock.patch('io.open', mock.mock_open(read_data='template content')) + def test_build_extra_template(self): + cfg = load_config() + files = Files([ + File('foo.html', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + ]) + build._build_extra_template('foo.html', files, cfg, mock.Mock()) - # Same as the default schema, but don't verify the docs_dir exists. - cfg = load_config( - site_name="Site", - extra={ - 'a': 1 - } + @mock.patch('io.open', mock.mock_open(read_data='template content')) + def test_skip_missing_extra_template(self): + cfg = load_config() + files = Files([ + File('foo.html', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + ]) + with self.assertLogs('mkdocs', level='INFO') as cm: + build._build_extra_template('missing.html', files, cfg, mock.Mock()) + self.assertEqual( + cm.output, + ["WARNING:mkdocs.commands.build:Template skipped: 'missing.html' not found in docs_dir."] ) - context = build.get_context(mock.Mock(), cfg) + @mock.patch('io.open', side_effect=IOError('Error message.')) + def test_skip_ioerror_extra_template(self, mock_open): + cfg = load_config() + files = Files([ + File('foo.html', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + ]) + with self.assertLogs('mkdocs', level='INFO') as cm: + build._build_extra_template('foo.html', files, cfg, mock.Mock()) + self.assertEqual( + cm.output, + ["WARNING:mkdocs.commands.build:Error reading template 'foo.html': Error message."] + ) - self.assertEqual(context['config']['extra']['a'], 1) + @mock.patch('io.open', mock.mock_open(read_data='')) + def test_skip_extra_template_empty_output(self): + cfg = load_config() + files = Files([ + File('foo.html', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + ]) + with self.assertLogs('mkdocs', level='INFO') as cm: + build._build_extra_template('foo.html', files, cfg, mock.Mock()) + self.assertEqual( + cm.output, + ["INFO:mkdocs.commands.build:Template skipped: 'foo.html' generated empty output."] + ) - def test_BOM(self): - with TemporaryDirectory() as docs_dir, TemporaryDirectory() as site_dir: - # Create an UTF-8 Encoded file with BOM (as Micorsoft editors do). See #1186. - f = io.open(os.path.join(docs_dir, 'index.md'), 'w', encoding='utf-8-sig') - f.write('# An UTF-8 encoded file with a BOM') - f.close() - - cfg = load_config( - docs_dir=docs_dir, - site_dir=site_dir - ) - build.build(cfg) - - # Verify that the file was generated properly. - # If the BOM is not removed, Markdown will return: - # `

    \ufeff# An UTF-8 encoded file with a BOM

    `. - f = io.open(os.path.join(site_dir, 'index.html'), 'r', encoding='utf-8') - output = f.read() - f.close() - self.assertTrue( - '

    An UTF-8 encoded file with a BOM

    ' in output - ) + # Test build._populate_page + + @tempdir(files={'index.md': 'page content'}) + def test_populate_page(self, docs_dir): + cfg = load_config(docs_dir=docs_dir) + file = File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + page = Page('Foo', file, cfg) + build._populate_page(page, cfg, Files([file])) + self.assertEqual(page.content, '

    page content

    ') + + @tempdir(files={'testing.html': '

    page content

    '}) + def test_populate_page_dirty_modified(self, site_dir): + cfg = load_config(site_dir=site_dir) + file = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + page = Page('Foo', file, cfg) + build._populate_page(page, cfg, Files([file]), dirty=True) + self.assertTrue(page.markdown.startswith('# Welcome to MkDocs')) + self.assertTrue(page.content.startswith('

    Welcome to MkDocs

    ')) + + @tempdir(files={'index.md': 'page content'}) + @tempdir(files={'index.html': '

    page content

    '}) + def test_populate_page_dirty_not_modified(self, site_dir, docs_dir): + cfg = load_config(docs_dir=docs_dir, site_dir=site_dir) + file = File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + page = Page('Foo', file, cfg) + build._populate_page(page, cfg, Files([file]), dirty=True) + # Content is empty as file read was skipped + self.assertEqual(page.markdown, None) + self.assertEqual(page.content, None) + + @tempdir(files={'index.md': 'new page content'}) + @mock.patch('io.open', side_effect=IOError('Error message.')) + def test_populate_page_read_error(self, docs_dir, mock_open): + cfg = load_config(docs_dir=docs_dir) + file = File('missing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + page = Page('Foo', file, cfg) + with self.assertLogs('mkdocs', level='ERROR') as cm: + self.assertRaises(IOError, build._populate_page, page, cfg, Files([file])) + self.assertEqual( + cm.output, [ + 'ERROR:mkdocs.structure.pages:File not found: missing.md', + "ERROR:mkdocs.commands.build:Error reading page 'missing.md': Error message." + ] + ) + mock_open.assert_called_once() + + # Test build._build_page + + @tempdir() + def test_build_page(self, site_dir): + cfg = load_config(site_dir=site_dir, nav=['index.md'], plugins=[]) + files = Files([File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])]) + nav = get_navigation(files, cfg) + page = files.documentation_pages()[0].page + # Fake populate page + page.title = 'Title' + page.markdown = 'page content' + page.content = '

    page content

    ' + build._build_page(page, cfg, files, nav, cfg['theme'].get_env()) + self.assertPathIsFile(site_dir, 'index.html') + + # TODO: fix this. It seems that jinja2 chokes on the mock object. Not sure how to resolve. + # @tempdir() + # @mock.patch('jinja2.environment.Template') + # def test_build_page_empty(self, site_dir, mock_template): + # mock_template.render = mock.Mock(return_value='') + # cfg = load_config(site_dir=site_dir, nav=['index.md'], plugins=[]) + # files = Files([File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])]) + # nav = get_navigation(files, cfg) + # page = files.documentation_pages()[0].page + # # Fake populate page + # page.title = '' + # page.markdown = '' + # page.content = '' + # with self.assertLogs('mkdocs', level='INFO') as cm: + # build._build_page(page, cfg, files, nav, cfg['theme'].get_env()) + # self.assertEqual( + # cm.output, + # ["INFO:mkdocs.commands.build:Page skipped: 'index.md'. Generated empty output."] + # ) + # mock_template.render.assert_called_once() + # self.assertPathNotFile(site_dir, 'index.html') + + @tempdir(files={'index.md': 'page content'}) + @tempdir(files={'index.html': '

    page content

    '}) + @mock.patch('mkdocs.utils.write_file') + def test_build_page_dirty_modified(self, site_dir, docs_dir, mock_write_file): + cfg = load_config(docs_dir=docs_dir, site_dir=site_dir, nav=['index.md'], plugins=[]) + files = Files([File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])]) + nav = get_navigation(files, cfg) + page = files.documentation_pages()[0].page + # Fake populate page + page.title = 'Title' + page.markdown = 'new page content' + page.content = '

    new page content

    ' + build._build_page(page, cfg, files, nav, cfg['theme'].get_env(), dirty=True) + mock_write_file.assert_not_called() + + @tempdir(files={'testing.html': '

    page content

    '}) + @mock.patch('mkdocs.utils.write_file') + def test_build_page_dirty_not_modified(self, site_dir, mock_write_file): + cfg = load_config(site_dir=site_dir, nav=['index.md'], plugins=[]) + files = Files([File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])]) + nav = get_navigation(files, cfg) + page = files.documentation_pages()[0].page + # Fake populate page + page.title = 'Title' + page.markdown = 'page content' + page.content = '

    page content

    ' + build._build_page(page, cfg, files, nav, cfg['theme'].get_env(), dirty=True) + mock_write_file.assert_called_once() + + @tempdir() + def test_build_page_custom_template(self, site_dir): + cfg = load_config(site_dir=site_dir, nav=['index.md'], plugins=[]) + files = Files([File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])]) + nav = get_navigation(files, cfg) + page = files.documentation_pages()[0].page + # Fake populate page + page.title = 'Title' + page.meta = {'template': '404.html'} + page.markdown = 'page content' + page.content = '

    page content

    ' + build._build_page(page, cfg, files, nav, cfg['theme'].get_env()) + self.assertPathIsFile(site_dir, 'index.html') + + @tempdir() + @mock.patch('mkdocs.utils.write_file', side_effect=IOError('Error message.')) + def test_build_page_error(self, site_dir, mock_write_file): + cfg = load_config(site_dir=site_dir, nav=['index.md'], plugins=[]) + files = Files([File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])]) + nav = get_navigation(files, cfg) + page = files.documentation_pages()[0].page + # Fake populate page + page.title = 'Title' + page.markdown = 'page content' + page.content = '

    page content

    ' + with self.assertLogs('mkdocs', level='ERROR') as cm: + self.assertRaises(IOError, build._build_page, page, cfg, files, nav, cfg['theme'].get_env()) + self.assertEqual( + cm.output, + ["ERROR:mkdocs.commands.build:Error building page 'index.md': Error message."] + ) + mock_write_file.assert_called_once() + + # Test build.build + + @tempdir(files={ + 'index.md': 'page content', + 'empty.md': '', + 'img.jpg': '', + 'static.html': 'content', + '.hidden': 'content', + '.git/hidden': 'content' + }) + @tempdir() + def test_copying_media(self, site_dir, docs_dir): + cfg = load_config(docs_dir=docs_dir, site_dir=site_dir) + build.build(cfg) + + # Verify that only non-empty md file (coverted to html), static HTML file and image are copied. + self.assertPathIsFile(site_dir, 'index.html') + self.assertPathIsFile(site_dir, 'img.jpg') + self.assertPathIsFile(site_dir, 'static.html') + self.assertPathNotExists(site_dir, 'empty.md') + self.assertPathNotExists(site_dir, '.hidden') + self.assertPathNotExists(site_dir, '.git/hidden') + + @tempdir(files={'index.md': 'page content'}) + @tempdir() + def test_copy_theme_files(self, site_dir, docs_dir): + cfg = load_config(docs_dir=docs_dir, site_dir=site_dir) + build.build(cfg) + + # Verify only theme media are copied, not templates or Python files. + self.assertPathIsFile(site_dir, 'index.html') + self.assertPathIsFile(site_dir, '404.html') + self.assertPathIsDir(site_dir, 'js') + self.assertPathIsDir(site_dir, 'css') + self.assertPathIsDir(site_dir, 'img') + self.assertPathIsDir(site_dir, 'fonts') + self.assertPathNotExists(site_dir, '__init__.py') + self.assertPathNotExists(site_dir, '__init__.pyc') + self.assertPathNotExists(site_dir, 'base.html') + self.assertPathNotExists(site_dir, 'content.html') + self.assertPathNotExists(site_dir, 'main.html') + + # Test build.site_directory_contains_stale_files + + @tempdir(files=['index.html']) + def test_site_dir_contains_stale_files(self, site_dir): + self.assertTrue(build.site_directory_contains_stale_files(site_dir)) + + @tempdir() + def test_not_site_dir_contains_stale_files(self, site_dir): + self.assertFalse(build.site_directory_contains_stale_files(site_dir)) diff --git a/mkdocs/tests/config/base_tests.py b/mkdocs/tests/config/base_tests.py index 5118279fe7..0d5e27fafa 100644 --- a/mkdocs/tests/config/base_tests.py +++ b/mkdocs/tests/config/base_tests.py @@ -9,7 +9,7 @@ except ImportError: from backports.tempfile import TemporaryDirectory -from mkdocs import exceptions +from mkdocs import exceptions, utils from mkdocs.config import base, defaults from mkdocs.config.config_options import BaseConfigOption @@ -273,5 +273,7 @@ def test_load_from_file_with_relative_paths(self): self.assertTrue(isinstance(cfg, base.Config)) self.assertEqual(cfg['site_name'], 'MkDocs Test') self.assertEqual(cfg['docs_dir'], docs_dir) + self.assertEqual(cfg.config_file_path, config_fname) + self.assertIsInstance(cfg.config_file_path, utils.text_type) finally: config_dir.cleanup() diff --git a/mkdocs/tests/config/config_options_tests.py b/mkdocs/tests/config/config_options_tests.py index 1d573ba244..cd828e4add 100644 --- a/mkdocs/tests/config/config_options_tests.py +++ b/mkdocs/tests/config/config_options_tests.py @@ -1,6 +1,9 @@ +# coding=UTF-8 + from __future__ import unicode_literals import os +import sys import unittest from mock import patch @@ -273,14 +276,99 @@ def test_incorrect_type_type_error(self): self.assertRaises(config_options.ValidationError, option.validate, []) - def test_doc_dir_is_config_dir(self): + def test_dir_unicode(self): + cfg = Config( + [('dir', config_options.Dir())], + config_file_path=os.path.join(os.path.abspath('.'), 'mkdocs.yml'), + ) + + test_config = { + 'dir': 'юникод' + } + + cfg.load_dict(test_config) + + fails, warns = cfg.validate() + + self.assertEqual(len(fails), 0) + self.assertEqual(len(warns), 0) + self.assertIsInstance(cfg['dir'], utils.text_type) + + def test_dir_filesystemencoding(self): + cfg = Config( + [('dir', config_options.Dir())], + config_file_path=os.path.join(os.path.abspath('.'), 'mkdocs.yml'), + ) + + test_config = { + 'dir': 'Übersicht'.encode(encoding=sys.getfilesystemencoding()) + } + + cfg.load_dict(test_config) + + fails, warns = cfg.validate() + + if utils.PY3: + # In PY3 string_types does not include byte strings so validation fails + self.assertEqual(len(fails), 1) + self.assertEqual(len(warns), 0) + else: + # In PY2 string_types includes byte strings so validation passes + # This test confirms that the byte string is properly decoded + self.assertEqual(len(fails), 0) + self.assertEqual(len(warns), 0) + self.assertIsInstance(cfg['dir'], utils.text_type) + + def test_dir_bad_encoding_fails(self): + cfg = Config( + [('dir', config_options.Dir())], + config_file_path=os.path.join(os.path.abspath('.'), 'mkdocs.yml'), + ) + + test_config = { + 'dir': 'юникод'.encode(encoding='ISO 8859-5') + } + + cfg.load_dict(test_config) + + fails, warns = cfg.validate() + + if sys.platform.startswith('win') and not utils.PY3: + # PY2 on Windows seems to be able to decode anything we give it. + # But that just means less possable errors for those users so we allow it. + self.assertEqual(len(fails), 0) + else: + self.assertEqual(len(fails), 1) + self.assertEqual(len(warns), 0) + + def test_config_dir_prepended(self): + base_path = os.path.abspath('.') + cfg = Config( + [('dir', config_options.Dir())], + config_file_path=os.path.join(base_path, 'mkdocs.yml'), + ) + + test_config = { + 'dir': 'foo' + } + + cfg.load_dict(test_config) + + fails, warns = cfg.validate() + + self.assertEqual(len(fails), 0) + self.assertEqual(len(warns), 0) + self.assertIsInstance(cfg['dir'], utils.text_type) + self.assertEqual(cfg['dir'], os.path.join(base_path, 'foo')) + + def test_dir_is_config_dir_fails(self): cfg = Config( - [('docs_dir', config_options.Dir())], + [('dir', config_options.Dir())], config_file_path=os.path.join(os.path.abspath('.'), 'mkdocs.yml'), ) test_config = { - 'docs_dir': '.' + 'dir': '.' } cfg.load_dict(test_config) @@ -438,11 +526,11 @@ def test_theme_invalid_type(self): option.validate, config) -class PagesTest(unittest.TestCase): +class NavTest(unittest.TestCase): def test_old_format(self): - option = config_options.Pages() + option = config_options.Nav() self.assertRaises( config_options.ValidationError, option.validate, @@ -451,7 +539,7 @@ def test_old_format(self): def test_provided_dict(self): - option = config_options.Pages() + option = config_options.Nav() value = option.validate([ 'index.md', {"Page": "page.md"} @@ -462,7 +550,7 @@ def test_provided_dict(self): def test_provided_empty(self): - option = config_options.Pages() + option = config_options.Nav() value = option.validate([]) self.assertEqual(None, value) @@ -470,13 +558,13 @@ def test_provided_empty(self): def test_invalid_type(self): - option = config_options.Pages() + option = config_options.Nav() self.assertRaises(config_options.ValidationError, option.validate, {}) def test_invalid_config(self): - option = config_options.Pages() + option = config_options.Nav() self.assertRaises(config_options.ValidationError, option.validate, [[], 1]) diff --git a/mkdocs/tests/config/config_tests.py b/mkdocs/tests/config/config_tests.py index 68c76c9484..2ad0fb54b3 100644 --- a/mkdocs/tests/config/config_tests.py +++ b/mkdocs/tests/config/config_tests.py @@ -178,56 +178,37 @@ def test_theme(self): self.assertEqual(c['theme'].static_templates, set(result['static_templates'])) self.assertEqual(dict([(k, c['theme'][k]) for k in iter(c['theme'])]), result['vars']) - def test_default_pages(self): - with TemporaryDirectory() as tmp_dir: - open(os.path.join(tmp_dir, 'index.md'), 'w').close() - open(os.path.join(tmp_dir, 'about.md'), 'w').close() - conf = config.Config(schema=config.DEFAULT_SCHEMA) - conf.load_dict({ - 'site_name': 'Example', - 'docs_dir': tmp_dir, - 'config_file_path': os.path.join(os.path.abspath('.'), 'mkdocs.yml') - }) - conf.validate() - self.assertEqual(['index.md', 'about.md'], conf['pages']) - - def test_default_pages_nested(self): - with TemporaryDirectory() as tmp_dir: - open(os.path.join(tmp_dir, 'index.md'), 'w').close() - open(os.path.join(tmp_dir, 'getting-started.md'), 'w').close() - open(os.path.join(tmp_dir, 'about.md'), 'w').close() - os.makedirs(os.path.join(tmp_dir, 'subA')) - open(os.path.join(tmp_dir, 'subA', 'index.md'), 'w').close() - os.makedirs(os.path.join(tmp_dir, 'subA', 'subA1')) - open(os.path.join(tmp_dir, 'subA', 'subA1', 'index.md'), 'w').close() - os.makedirs(os.path.join(tmp_dir, 'subC')) - open(os.path.join(tmp_dir, 'subC', 'index.md'), 'w').close() - os.makedirs(os.path.join(tmp_dir, 'subB')) - open(os.path.join(tmp_dir, 'subB', 'index.md'), 'w').close() - conf = config.Config(schema=config.DEFAULT_SCHEMA) - conf.load_dict({ - 'site_name': 'Example', - 'docs_dir': tmp_dir, - 'config_file_path': os.path.join(os.path.abspath('.'), 'mkdocs.yml') - }) - conf.validate() - self.assertEqual([ - 'index.md', - 'about.md', - 'getting-started.md', - {'subA': [ - os.path.join('subA', 'index.md'), - {'subA1': [ - os.path.join('subA', 'subA1', 'index.md') - ]} - ]}, - {'subB': [ - os.path.join('subB', 'index.md') - ]}, - {'subC': [ - os.path.join('subC', 'index.md') - ]} - ], conf['pages']) + def test_empty_nav(self): + conf = config.Config(schema=config.DEFAULT_SCHEMA) + conf.load_dict({ + 'site_name': 'Example', + 'config_file_path': os.path.join(os.path.abspath('.'), 'mkdocs.yml') + }) + conf.validate() + self.assertEqual(conf['nav'], None) + + def test_copy_pages_to_nav(self): + # TODO: remove this when pages config setting is fully deprecated. + conf = config.Config(schema=config.DEFAULT_SCHEMA) + conf.load_dict({ + 'site_name': 'Example', + 'pages': ['index.md', 'about.md'], + 'config_file_path': os.path.join(os.path.abspath('.'), 'mkdocs.yml') + }) + conf.validate() + self.assertEqual(conf['nav'], ['index.md', 'about.md']) + + def test_dont_overwrite_nav_with_pages(self): + # TODO: remove this when pages config setting is fully deprecated. + conf = config.Config(schema=config.DEFAULT_SCHEMA) + conf.load_dict({ + 'site_name': 'Example', + 'pages': ['index.md', 'about.md'], + 'nav': ['foo.md', 'bar.md'], + 'config_file_path': os.path.join(os.path.abspath('.'), 'mkdocs.yml') + }) + conf.validate() + self.assertEqual(conf['nav'], ['foo.md', 'bar.md']) def test_doc_dir_in_site_dir(self): diff --git a/mkdocs/tests/integration/complicated_config/mkdocs.yml b/mkdocs/tests/integration/complicated_config/mkdocs.yml index f84936cc25..5c20379555 100644 --- a/mkdocs/tests/integration/complicated_config/mkdocs.yml +++ b/mkdocs/tests/integration/complicated_config/mkdocs.yml @@ -1,6 +1,6 @@ site_name: My Docs -pages: +nav: - Home: index.md - User Guide: - Writing your docs: index.md diff --git a/mkdocs/tests/integration/minimal/mkdocs.yml b/mkdocs/tests/integration/minimal/mkdocs.yml index f4d5b08c94..ff21753366 100644 --- a/mkdocs/tests/integration/minimal/mkdocs.yml +++ b/mkdocs/tests/integration/minimal/mkdocs.yml @@ -1,6 +1,6 @@ site_name: MyTest -pages: +nav: - 'testing.md' site_author: "Tom Christie & Dougal Matthews" diff --git a/mkdocs/tests/integration/subpages/docs/index.md b/mkdocs/tests/integration/subpages/docs/index.md index e4801fa6f4..9700881581 100644 --- a/mkdocs/tests/integration/subpages/docs/index.md +++ b/mkdocs/tests/integration/subpages/docs/index.md @@ -1,4 +1,4 @@ -# Test sub pages and referencing images +## Test sub pages and referencing images ## Reference an image in: / diff --git a/mkdocs/tests/integration/subpages/docs/metadata.md b/mkdocs/tests/integration/subpages/docs/metadata.md new file mode 100644 index 0000000000..d871ea26db --- /dev/null +++ b/mkdocs/tests/integration/subpages/docs/metadata.md @@ -0,0 +1,5 @@ +title: A Page Title + +# Welcome to MkDocs + +Some page content goes here. diff --git a/mkdocs/tests/integration/subpages/docs/page-title.md b/mkdocs/tests/integration/subpages/docs/page-title.md new file mode 100644 index 0000000000..26075f1ca2 --- /dev/null +++ b/mkdocs/tests/integration/subpages/docs/page-title.md @@ -0,0 +1 @@ +Page content. diff --git a/mkdocs/tests/integration/subpages/docs/pageTitle.md b/mkdocs/tests/integration/subpages/docs/pageTitle.md new file mode 100644 index 0000000000..26075f1ca2 --- /dev/null +++ b/mkdocs/tests/integration/subpages/docs/pageTitle.md @@ -0,0 +1 @@ +Page content. diff --git "a/mkdocs/tests/integration/unicode/docs/\303\234bersicht.md" "b/mkdocs/tests/integration/unicode/docs/\303\234bersicht.md" index da37213adb..e410983437 100644 --- "a/mkdocs/tests/integration/unicode/docs/\303\234bersicht.md" +++ "b/mkdocs/tests/integration/unicode/docs/\303\234bersicht.md" @@ -1,4 +1,4 @@ -# Welcome to MkDocs +Welcome to MkDocs For full documentation visit [mkdocs.org](http://mkdocs.org). diff --git "a/mkdocs/tests/integration/unicode/docs/\342\231\252.md" "b/mkdocs/tests/integration/unicode/docs/\342\231\252.md" index da37213adb..e410983437 100644 --- "a/mkdocs/tests/integration/unicode/docs/\342\231\252.md" +++ "b/mkdocs/tests/integration/unicode/docs/\342\231\252.md" @@ -1,4 +1,4 @@ -# Welcome to MkDocs +Welcome to MkDocs For full documentation visit [mkdocs.org](http://mkdocs.org). diff --git a/mkdocs/tests/nav_tests.py b/mkdocs/tests/nav_tests.py deleted file mode 100644 index d8648b1ca3..0000000000 --- a/mkdocs/tests/nav_tests.py +++ /dev/null @@ -1,850 +0,0 @@ -#!/usr/bin/env python -# coding: utf-8 - -from __future__ import unicode_literals -import mock -import os -import unittest - -from mkdocs import nav -from mkdocs.exceptions import ConfigurationError -from mkdocs.tests.base import dedent, load_config - - -class SiteNavigationTests(unittest.TestCase): - def test_simple_toc(self): - pages = [ - {'Home': 'index.md'}, - {'About': 'about.md'} - ] - expected = dedent(""" - Home - / - About - /about/ - """) - site_navigation = nav.SiteNavigation(load_config(pages=pages)) - self.assertEqual(str(site_navigation).strip(), expected) - self.assertEqual(len(site_navigation.nav_items), 2) - self.assertEqual(len(site_navigation.pages), 2) - - def test_empty_toc_item(self): - pages = [ - 'index.md', - {'About': 'about.md'} - ] - expected = dedent(""" - Home - / - About - /about/ - """) - site_navigation = nav.SiteNavigation(load_config(pages=pages)) - self.assertEqual(str(site_navigation).strip(), expected) - self.assertEqual(len(site_navigation.nav_items), 2) - self.assertEqual(len(site_navigation.pages), 2) - - def test_indented_toc(self): - pages = [ - {'Home': 'index.md'}, - {'API Guide': [ - {'Running': 'api-guide/running.md'}, - {'Testing': 'api-guide/testing.md'}, - {'Debugging': 'api-guide/debugging.md'}, - ]}, - {'About': [ - {'Release notes': 'about/release-notes.md'}, - {'License': 'about/license.md'} - ]} - ] - expected = dedent(""" - Home - / - API Guide - Running - /api-guide/running/ - Testing - /api-guide/testing/ - Debugging - /api-guide/debugging/ - About - Release notes - /about/release-notes/ - License - /about/license/ - """) - site_navigation = nav.SiteNavigation(load_config(pages=pages)) - self.assertEqual(str(site_navigation).strip(), expected) - self.assertEqual(len(site_navigation.nav_items), 3) - self.assertEqual(len(site_navigation.pages), 6) - - def test_nested_ungrouped(self): - pages = [ - {'Home': 'index.md'}, - {'Contact': 'about/contact.md'}, - {'License Title': 'about/sub/license.md'}, - ] - expected = dedent(""" - Home - / - Contact - /about/contact/ - License Title - /about/sub/license/ - """) - site_navigation = nav.SiteNavigation(load_config(pages=pages)) - self.assertEqual(str(site_navigation).strip(), expected) - self.assertEqual(len(site_navigation.nav_items), 3) - self.assertEqual(len(site_navigation.pages), 3) - - def test_nested_ungrouped_no_titles(self): - pages = [ - 'index.md', - 'about/contact.md', - 'about/sub/license.md' - ] - expected = dedent(""" - Home - / - Contact - /about/contact/ - License - /about/sub/license/ - """) - - site_navigation = nav.SiteNavigation(load_config(pages=pages)) - self.assertEqual(str(site_navigation).strip(), expected) - self.assertEqual(len(site_navigation.nav_items), 3) - self.assertEqual(len(site_navigation.pages), 3) - - @mock.patch.object(os.path, 'sep', '\\') - def test_nested_ungrouped_no_titles_windows(self): - pages = [ - 'index.md', - 'about\\contact.md', - 'about\\sub\\license.md', - ] - expected = dedent(""" - Home - / - Contact - /about/contact/ - License - /about/sub/license/ - """) - - site_navigation = nav.SiteNavigation(load_config(pages=pages)) - self.assertEqual(str(site_navigation).strip(), expected) - self.assertEqual(len(site_navigation.nav_items), 3) - self.assertEqual(len(site_navigation.pages), 3) - - def test_walk_simple_toc(self): - pages = [ - {'Home': 'index.md'}, - {'About': 'about.md'} - ] - expected = [ - dedent(""" - Home - / [*] - About - /about/ - """), - dedent(""" - Home - / - About - /about/ [*] - """) - ] - site_navigation = nav.SiteNavigation(load_config(pages=pages)) - for index, page in enumerate(site_navigation.walk_pages()): - self.assertEqual(str(site_navigation).strip(), expected[index]) - - def test_walk_empty_toc(self): - pages = [ - 'index.md', - {'About': 'about.md'} - ] - expected = [ - dedent(""" - Home - / [*] - About - /about/ - """), - dedent(""" - Home - / - About - /about/ [*] - """) - ] - site_navigation = nav.SiteNavigation(load_config(pages=pages)) - for index, page in enumerate(site_navigation.walk_pages()): - self.assertEqual(str(site_navigation).strip(), expected[index]) - - def test_walk_indented_toc(self): - pages = [ - {'Home': 'index.md'}, - {'API Guide': [ - {'Running': 'api-guide/running.md'}, - {'Testing': 'api-guide/testing.md'}, - {'Debugging': 'api-guide/debugging.md'}, - ]}, - {'About': [ - {'Release notes': 'about/release-notes.md'}, - {'License': 'about/license.md'} - ]} - ] - expected = [ - dedent(""" - Home - / [*] - API Guide - Running - /api-guide/running/ - Testing - /api-guide/testing/ - Debugging - /api-guide/debugging/ - About - Release notes - /about/release-notes/ - License - /about/license/ - """), - dedent(""" - Home - / - API Guide [*] - Running - /api-guide/running/ [*] - Testing - /api-guide/testing/ - Debugging - /api-guide/debugging/ - About - Release notes - /about/release-notes/ - License - /about/license/ - """), - dedent(""" - Home - / - API Guide [*] - Running - /api-guide/running/ - Testing - /api-guide/testing/ [*] - Debugging - /api-guide/debugging/ - About - Release notes - /about/release-notes/ - License - /about/license/ - """), - dedent(""" - Home - / - API Guide [*] - Running - /api-guide/running/ - Testing - /api-guide/testing/ - Debugging - /api-guide/debugging/ [*] - About - Release notes - /about/release-notes/ - License - /about/license/ - """), - dedent(""" - Home - / - API Guide - Running - /api-guide/running/ - Testing - /api-guide/testing/ - Debugging - /api-guide/debugging/ - About [*] - Release notes - /about/release-notes/ [*] - License - /about/license/ - """), - dedent(""" - Home - / - API Guide - Running - /api-guide/running/ - Testing - /api-guide/testing/ - Debugging - /api-guide/debugging/ - About [*] - Release notes - /about/release-notes/ - License - /about/license/ [*] - """) - ] - site_navigation = nav.SiteNavigation(load_config(pages=pages)) - for index, page in enumerate(site_navigation.walk_pages()): - self.assertEqual(str(site_navigation).strip(), expected[index]) - - def test_base_url(self): - pages = [ - 'index.md' - ] - site_navigation = nav.SiteNavigation(load_config(pages=pages, use_directory_urls=False)) - base_url = site_navigation.url_context.make_relative('/') - self.assertEqual(base_url, '.') - - def test_relative_md_links_have_slash(self): - pages = [ - 'index.md', - 'user-guide/styling-your-docs.md' - ] - site_navigation = nav.SiteNavigation(load_config(pages=pages, use_directory_urls=False)) - site_navigation.url_context.base_path = "/user-guide/configuration" - url = site_navigation.url_context.make_relative('/user-guide/styling-your-docs/') - self.assertEqual(url, '../styling-your-docs/') - - def test_generate_site_navigation(self): - """ - Verify inferring page titles based on the filename - """ - - pages = [ - 'index.md', - 'api-guide/running.md', - 'about/notes.md', - 'about/sub/license.md', - ] - - url_context = nav.URLContext() - nav_items, pages = nav._generate_site_navigation(load_config(pages=pages), url_context) - - self.assertEqual([n.title for n in nav_items], - ['Home', 'Running', 'Notes', 'License']) - self.assertEqual([n.url for n in nav_items], [ - '.', - 'api-guide/running/', - 'about/notes/', - 'about/sub/license/' - ]) - self.assertEqual([p.title for p in pages], - ['Home', 'Running', 'Notes', 'License']) - - @mock.patch.object(os.path, 'sep', '\\') - def test_generate_site_navigation_windows(self): - """ - Verify inferring page titles based on the filename with a windows path - """ - pages = [ - 'index.md', - 'api-guide\\running.md', - 'about\\notes.md', - 'about\\sub\\license.md', - ] - - url_context = nav.URLContext() - nav_items, pages = nav._generate_site_navigation(load_config(pages=pages), url_context) - - self.assertEqual([n.title for n in nav_items], - ['Home', 'Running', 'Notes', 'License']) - self.assertEqual([n.url for n in nav_items], [ - '.', - 'api-guide/running/', - 'about/notes/', - 'about/sub/license/' - ]) - self.assertEqual([p.title for p in pages], - ['Home', 'Running', 'Notes', 'License']) - - def test_force_abs_urls(self): - """ - Verify force absolute URLs - """ - - pages = [ - 'index.md', - 'api-guide/running.md', - 'about/notes.md', - 'about/sub/license.md', - ] - - url_context = nav.URLContext() - url_context.force_abs_urls = True - nav_items, pages = nav._generate_site_navigation(load_config(pages=pages), url_context) - - self.assertEqual([n.title for n in nav_items], - ['Home', 'Running', 'Notes', 'License']) - self.assertEqual([n.url for n in nav_items], [ - '/', - '/api-guide/running/', - '/about/notes/', - '/about/sub/license/' - ]) - - def test_force_abs_urls_with_base(self): - """ - Verify force absolute URLs - """ - - pages = [ - 'index.md', - 'api-guide/running.md', - 'about/notes.md', - 'about/sub/license.md', - ] - - url_context = nav.URLContext() - url_context.force_abs_urls = True - url_context.base_path = '/foo/' - nav_items, pages = nav._generate_site_navigation(load_config(pages=pages), url_context) - - self.assertEqual([n.title for n in nav_items], - ['Home', 'Running', 'Notes', 'License']) - self.assertEqual([n.url for n in nav_items], [ - '/foo/', - '/foo/api-guide/running/', - '/foo/about/notes/', - '/foo/about/sub/license/' - ]) - - def test_invalid_pages_config(self): - - bad_page = {"a": "index.md", "b": "index.md"} # extra key - - def _test(): - return nav._generate_site_navigation(load_config(pages=[bad_page, ]), None) - - self.assertRaises(ConfigurationError, _test) - - def test_pages_config(self): - - bad_page = {} # empty - - def _test(): - return nav._generate_site_navigation(load_config(pages=[bad_page, ]), None) - - self.assertRaises(ConfigurationError, _test) - - def test_ancestors(self): - - pages = [ - {'Home': 'index.md'}, - {'API Guide': [ - {'Running': 'api-guide/running.md'}, - {'Testing': 'api-guide/testing.md'}, - {'Debugging': 'api-guide/debugging.md'}, - {'Advanced': [ - {'Part 1': 'api-guide/advanced/part-1.md'}, - ]}, - ]}, - {'About': [ - {'Release notes': 'about/release-notes.md'}, - {'License': 'about/license.md'} - ]} - ] - site_navigation = nav.SiteNavigation(load_config(pages=pages)) - - ancestors = ( - [], - [site_navigation.nav_items[1]], - [site_navigation.nav_items[1]], - [site_navigation.nav_items[1]], - [site_navigation.nav_items[1], - site_navigation.pages[4].ancestors[-1]], - [site_navigation.nav_items[2]], - [site_navigation.nav_items[2]], - ) - - self.assertEqual(len(site_navigation.pages), len(ancestors)) - - for i, (page, expected_ancestor) in enumerate( - zip(site_navigation.pages, ancestors)): - self.assertEqual(page.ancestors, expected_ancestor, - "Failed on ancestor test {0}".format(i)) - - def test_nesting(self): - - pages = [ - {'Home': 'index.md'}, - {'Install': [ - {'Pre-install': 'install/install-pre.md'}, - {'The install': 'install/install-actual.md'}, - {'Post install': 'install/install-post.md'}, - ]}, - {'Guide': [ - {'Tutorial': [ - {'Getting Started': 'guide/tutorial/running.md'}, - {'Advanced Features': 'guide/tutorial/testing.md'}, - {'Further Reading': 'guide/tutorial/debugging.md'}, - ]}, - {'API Reference': [ - {'Feature 1': 'guide/api-ref/running.md'}, - {'Feature 2': 'guide/api-ref/testing.md'}, - {'Feature 3': 'guide/api-ref/debugging.md'}, - ]}, - {'Testing': 'guide/testing.md'}, - {'Deploying': 'guide/deploying.md'}, - ]} - ] - - site_navigation = nav.SiteNavigation(load_config(pages=pages)) - - self.assertEqual([n.title for n in site_navigation.nav_items], - ['Home', 'Install', 'Guide']) - self.assertEqual(len(site_navigation.pages), 12) - - expected = dedent(""" - Home - / - Install - Pre-install - /install/install-pre/ - The install - /install/install-actual/ - Post install - /install/install-post/ - Guide - Tutorial - Getting Started - /guide/tutorial/running/ - Advanced Features - /guide/tutorial/testing/ - Further Reading - /guide/tutorial/debugging/ - API Reference - Feature 1 - /guide/api-ref/running/ - Feature 2 - /guide/api-ref/testing/ - Feature 3 - /guide/api-ref/debugging/ - Testing - /guide/testing/ - Deploying - /guide/deploying/ - """) - - self.maxDiff = None - self.assertEqual(str(site_navigation).strip(), expected) - - def test_edit_uri(self): - - pages = [ - 'index.md', - 'internal.md', - 'sub/internal.md', - 'sub1/sub2/internal.md', - ] - - # Basic test - repo_url = 'http://example.com/' - edit_uri = 'edit/master/docs/' - - site_navigation = nav.SiteNavigation(load_config( - pages=pages, - repo_url=repo_url, - edit_uri=edit_uri, - site_dir='site', - site_url='', - use_directory_urls=True - )) - - expected_results = ( - repo_url + edit_uri + pages[0], - repo_url + edit_uri + pages[1], - repo_url + edit_uri + pages[2], - repo_url + edit_uri + pages[3], - ) - - for idx, page in enumerate(site_navigation.walk_pages()): - self.assertEqual(page.edit_url, expected_results[idx]) - - def test_edit_uri_sub_dir(self): - - pages = [ - 'index.md', - 'internal.md', - 'sub/internal.md', - 'sub1/sub2/internal.md', - ] - - # Basic test - repo_url = 'http://example.com/foo/' - edit_uri = 'edit/master/docs/' - - site_navigation = nav.SiteNavigation(load_config( - pages=pages, - repo_url=repo_url, - edit_uri=edit_uri, - site_dir='site', - site_url='', - use_directory_urls=True - )) - - expected_results = ( - repo_url + edit_uri + pages[0], - repo_url + edit_uri + pages[1], - repo_url + edit_uri + pages[2], - repo_url + edit_uri + pages[3], - ) - - for idx, page in enumerate(site_navigation.walk_pages()): - self.assertEqual(page.edit_url, expected_results[idx]) - - def test_edit_uri_missing_slash(self): - - pages = [ - 'index.md', - 'internal.md', - 'sub/internal.md', - 'sub1/sub2/internal.md', - ] - - # Ensure the '/' is added to the repo_url and edit_uri - repo_url = 'http://example.com' - edit_uri = 'edit/master/docs' - - site_navigation = nav.SiteNavigation(load_config( - pages=pages, - repo_url=repo_url, - edit_uri=edit_uri, - site_dir='site', - site_url='', - use_directory_urls=True - )) - - expected_results = ( - repo_url + '/' + edit_uri + '/' + pages[0], - repo_url + '/' + edit_uri + '/' + pages[1], - repo_url + '/' + edit_uri + '/' + pages[2], - repo_url + '/' + edit_uri + '/' + pages[3], - ) - - for idx, page in enumerate(site_navigation.walk_pages()): - self.assertEqual(page.edit_url, expected_results[idx]) - - def test_edit_uri_sub_dir_missing_slash(self): - - pages = [ - 'index.md', - 'internal.md', - 'sub/internal.md', - 'sub1/sub2/internal.md', - ] - - # Basic test - repo_url = 'http://example.com/foo' - edit_uri = 'edit/master/docs' - - site_navigation = nav.SiteNavigation(load_config( - pages=pages, - repo_url=repo_url, - edit_uri=edit_uri, - site_dir='site', - site_url='', - use_directory_urls=True - )) - - expected_results = ( - repo_url + '/' + edit_uri + '/' + pages[0], - repo_url + '/' + edit_uri + '/' + pages[1], - repo_url + '/' + edit_uri + '/' + pages[2], - repo_url + '/' + edit_uri + '/' + pages[3], - ) - - for idx, page in enumerate(site_navigation.walk_pages()): - self.assertEqual(page.edit_url, expected_results[idx]) - - def test_edit_uri_query_string(self): - - pages = [ - 'index.md', - 'internal.md', - 'sub/internal.md', - 'sub1/sub2/internal.md', - ] - - # Ensure query strings are supported - repo_url = 'http://example.com' - edit_uri = '?query=edit/master/docs/' - - site_navigation = nav.SiteNavigation(load_config( - pages=pages, - repo_url=repo_url, - edit_uri=edit_uri, - site_dir='site', - site_url='', - use_directory_urls=True - )) - - expected_results = ( - repo_url + edit_uri + pages[0], - repo_url + edit_uri + pages[1], - repo_url + edit_uri + pages[2], - repo_url + edit_uri + pages[3], - ) - - for idx, page in enumerate(site_navigation.walk_pages()): - self.assertEqual(page.edit_url, expected_results[idx]) - - def test_edit_uri_fragment(self): - - pages = [ - 'index.md', - 'internal.md', - 'sub/internal.md', - 'sub1/sub2/internal.md', - ] - - # Ensure fragment strings are supported - repo_url = 'http://example.com' - edit_uri = '#fragment/edit/master/docs/' - - site_navigation = nav.SiteNavigation(load_config( - pages=pages, - repo_url=repo_url, - edit_uri=edit_uri, - site_dir='site', - site_url='', - use_directory_urls=True - )) - - expected_results = ( - repo_url + edit_uri + pages[0], - repo_url + edit_uri + pages[1], - repo_url + edit_uri + pages[2], - repo_url + edit_uri + pages[3], - ) - - for idx, page in enumerate(site_navigation.walk_pages()): - self.assertEqual(page.edit_url, expected_results[idx]) - - def test_edit_uri_windows(self): - - pages = [ - 'index.md', - 'internal.md', - 'sub\\internal.md', - 'sub1\\sub2\\internal.md', - ] - - # Basic test - repo_url = 'http://example.com/' - edit_uri = 'edit/master/docs/' - - site_navigation = nav.SiteNavigation(load_config( - pages=pages, - repo_url=repo_url, - edit_uri=edit_uri, - site_dir='site', - site_url='', - use_directory_urls=True - )) - - expected_results = ( - repo_url + edit_uri + pages[0], - repo_url + edit_uri + pages[1], - repo_url + edit_uri + pages[2].replace('\\', '/'), - repo_url + edit_uri + pages[3].replace('\\', '/'), - ) - - for idx, page in enumerate(site_navigation.walk_pages()): - self.assertEqual(page.edit_url, expected_results[idx]) - - def test_edit_uri_sub_dir_windows(self): - - pages = [ - 'index.md', - 'internal.md', - 'sub\\internal.md', - 'sub1\\sub2\\internal.md', - ] - - # Basic test - repo_url = 'http://example.com/foo/' - edit_uri = 'edit/master/docs/' - - site_navigation = nav.SiteNavigation(load_config( - pages=pages, - repo_url=repo_url, - edit_uri=edit_uri, - site_dir='site', - site_url='', - use_directory_urls=True - )) - - expected_results = ( - repo_url + edit_uri + pages[0], - repo_url + edit_uri + pages[1], - repo_url + edit_uri + pages[2].replace('\\', '/'), - repo_url + edit_uri + pages[3].replace('\\', '/'), - ) - - for idx, page in enumerate(site_navigation.walk_pages()): - self.assertEqual(page.edit_url, expected_results[idx]) - - def test_edit_uri_missing_slash_windows(self): - - pages = [ - 'index.md', - 'internal.md', - 'sub\\internal.md', - 'sub1\\sub2\\internal.md', - ] - - # Ensure the '/' is added to the repo_url and edit_uri - repo_url = 'http://example.com' - edit_uri = 'edit/master/docs' - - site_navigation = nav.SiteNavigation(load_config( - pages=pages, - repo_url=repo_url, - edit_uri=edit_uri, - site_dir='site', - site_url='', - use_directory_urls=True - )) - - expected_results = ( - repo_url + '/' + edit_uri + '/' + pages[0], - repo_url + '/' + edit_uri + '/' + pages[1], - repo_url + '/' + edit_uri + '/' + pages[2].replace('\\', '/'), - repo_url + '/' + edit_uri + '/' + pages[3].replace('\\', '/'), - ) - - for idx, page in enumerate(site_navigation.walk_pages()): - self.assertEqual(page.edit_url, expected_results[idx]) - - def test_edit_uri_sub_dir_missing_slash_windows(self): - - pages = [ - 'index.md', - 'internal.md', - 'sub\\internal.md', - 'sub1\\sub2\\internal.md', - ] - - # Ensure the '/' is added to the repo_url and edit_uri - repo_url = 'http://example.com/foo' - edit_uri = 'edit/master/docs' - - site_navigation = nav.SiteNavigation(load_config( - pages=pages, - repo_url=repo_url, - edit_uri=edit_uri, - site_dir='site', - site_url='', - use_directory_urls=True - )) - - expected_results = ( - repo_url + '/' + edit_uri + '/' + pages[0], - repo_url + '/' + edit_uri + '/' + pages[1], - repo_url + '/' + edit_uri + '/' + pages[2].replace('\\', '/'), - repo_url + '/' + edit_uri + '/' + pages[3].replace('\\', '/'), - ) - - for idx, page in enumerate(site_navigation.walk_pages()): - self.assertEqual(page.edit_url, expected_results[idx]) - - def test_edit_uri_query_string_windows(self): - - pages = [ - 'index.md', - 'internal.md', - 'sub\\internal.md', - 'sub1\\sub2\\internal.md', - ] - - # Ensure query strings are supported - repo_url = 'http://example.com' - edit_uri = '?query=edit/master/docs/' - - site_navigation = nav.SiteNavigation(load_config( - pages=pages, - repo_url=repo_url, - edit_uri=edit_uri, - site_dir='site', - site_url='', - use_directory_urls=True - )) - - expected_results = ( - repo_url + edit_uri + pages[0], - repo_url + edit_uri + pages[1], - repo_url + edit_uri + pages[2].replace('\\', '/'), - repo_url + edit_uri + pages[3].replace('\\', '/'), - ) - - for idx, page in enumerate(site_navigation.walk_pages()): - self.assertEqual(page.edit_url, expected_results[idx]) - - def test_edit_uri_fragment_windows(self): - - pages = [ - 'index.md', - 'internal.md', - 'sub\\internal.md', - 'sub1\\sub2\\internal.md', - ] - - # Ensure fragment strings are supported - repo_url = 'http://example.com' - edit_uri = '#fragment/edit/master/docs/' - - site_navigation = nav.SiteNavigation(load_config( - pages=pages, - repo_url=repo_url, - edit_uri=edit_uri, - site_dir='site', - site_url='', - use_directory_urls=True - )) - - expected_results = ( - repo_url + edit_uri + pages[0], - repo_url + edit_uri + pages[1], - repo_url + edit_uri + pages[2].replace('\\', '/'), - repo_url + edit_uri + pages[3].replace('\\', '/'), - ) - - for idx, page in enumerate(site_navigation.walk_pages()): - self.assertEqual(page.edit_url, expected_results[idx]) diff --git a/mkdocs/tests/search_tests.py b/mkdocs/tests/search_tests.py index 2444b8efed..8441e61bdb 100644 --- a/mkdocs/tests/search_tests.py +++ b/mkdocs/tests/search_tests.py @@ -6,11 +6,13 @@ import mock import json -from mkdocs import nav +from mkdocs.structure.files import File +from mkdocs.structure.pages import Page +from mkdocs.structure.toc import get_toc from mkdocs.contrib import search from mkdocs.contrib.search import search_index from mkdocs.config.config_options import ValidationError -from mkdocs.tests.base import dedent, markdown_to_toc, load_config +from mkdocs.tests.base import dedent, get_markdown_toc, load_config def strip_whitespace(string): @@ -248,7 +250,7 @@ def test_find_toc_by_id(self): ## Heading 2 ### Heading 3 """) - toc = markdown_to_toc(md) + toc = get_toc(get_markdown_toc(md)) toc_item = index._find_toc_by_id(toc, "heading-1") self.assertEqual(toc_item.url, "#heading-1") @@ -273,23 +275,22 @@ def test_create_search_index(self):

    Content 3

    """ + cfg = load_config() pages = [ - {'Home': 'index.md'}, - {'About': 'about.md'}, + Page('Home', File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), cfg), + Page('About', File('about.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), cfg) ] - site_navigation = nav.SiteNavigation(load_config(pages=pages)) - md = dedent(""" # Heading 1 ## Heading 2 ### Heading 3 """) - toc = markdown_to_toc(md) + toc = get_toc(get_markdown_toc(md)) full_content = ''.join("""Heading{0}Content{0}""".format(i) for i in range(1, 4)) - for page in site_navigation: + for page in pages: # Fake page.read_source() and page.render() page.markdown = md page.toc = toc @@ -300,7 +301,7 @@ def test_create_search_index(self): self.assertEqual(len(index._entries), 4) - loc = page.abs_url + loc = page.url self.assertEqual(index._entries[0]['title'], page.title) self.assertEqual(strip_whitespace(index._entries[0]['text']), full_content) diff --git a/mkdocs/tests/structure/__init__.py b/mkdocs/tests/structure/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/mkdocs/tests/structure/file_tests.py b/mkdocs/tests/structure/file_tests.py new file mode 100644 index 0000000000..3e56bea1da --- /dev/null +++ b/mkdocs/tests/structure/file_tests.py @@ -0,0 +1,576 @@ +import unittest +import os +import io +import mock + +from mkdocs.structure.files import Files, File, get_files, _sort_files, _filter_paths +from mkdocs.tests.base import load_config, tempdir, PathAssertionMixin + + +class TestFiles(PathAssertionMixin, unittest.TestCase): + + def test_file_eq(self): + file = File('a.md', '/path/to/docs', '/path/to/site', use_directory_urls=False) + self.assertTrue(file == File('a.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)) + + def test_file_ne(self): + file = File('a.md', '/path/to/docs', '/path/to/site', use_directory_urls=False) + # Different filename + self.assertTrue(file != File('b.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)) + # Different src_path + self.assertTrue(file != File('a.md', '/path/to/other', '/path/to/site', use_directory_urls=False)) + # Different URL + self.assertTrue(file != File('a.md', '/path/to/docs', '/path/to/site', use_directory_urls=True)) + + def test_sort_files(self): + self.assertEqual( + _sort_files(['b.md', 'bb.md', 'a.md', 'index.md', 'aa.md']), + ['index.md', 'a.md', 'aa.md', 'b.md', 'bb.md'] + ) + + self.assertEqual( + _sort_files(['b.md', 'index.html', 'a.md', 'index.md']), + ['index.html', 'index.md', 'a.md', 'b.md'] + ) + + self.assertEqual( + _sort_files(['a.md', 'index.md', 'b.md', 'index.html']), + ['index.md', 'index.html', 'a.md', 'b.md'] + ) + + self.assertEqual( + _sort_files(['.md', '_.md', 'a.md', 'index.md', '1.md']), + ['index.md', '.md', '1.md', '_.md', 'a.md'] + ) + + self.assertEqual( + _sort_files(['a.md', 'b.md', 'a.md']), + ['a.md', 'a.md', 'b.md'] + ) + + def test_md_file(self): + f = File('foo.md', '/path/to/docs', '/path/to/site', use_directory_urls=False) + self.assertPathsEqual(f.src_path, 'foo.md') + self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo.md') + self.assertPathsEqual(f.dest_path, 'foo.html') + self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo.html') + self.assertEqual(f.url, 'foo.html') + self.assertEqual(f.name, 'foo') + self.assertTrue(f.is_documentation_page()) + self.assertFalse(f.is_static_page()) + self.assertFalse(f.is_media_file()) + self.assertFalse(f.is_javascript()) + self.assertFalse(f.is_css()) + + def test_md_file_use_directory_urls(self): + f = File('foo.md', '/path/to/docs', '/path/to/site', use_directory_urls=True) + self.assertPathsEqual(f.src_path, 'foo.md') + self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo.md') + self.assertPathsEqual(f.dest_path, 'foo/index.html') + self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/index.html') + self.assertEqual(f.url, 'foo/') + self.assertEqual(f.name, 'foo') + self.assertTrue(f.is_documentation_page()) + self.assertFalse(f.is_static_page()) + self.assertFalse(f.is_media_file()) + self.assertFalse(f.is_javascript()) + self.assertFalse(f.is_css()) + + def test_md_file_nested(self): + f = File('foo/bar.md', '/path/to/docs', '/path/to/site', use_directory_urls=False) + self.assertPathsEqual(f.src_path, 'foo/bar.md') + self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.md') + self.assertPathsEqual(f.dest_path, 'foo/bar.html') + self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.html') + self.assertEqual(f.url, 'foo/bar.html') + self.assertEqual(f.name, 'bar') + self.assertTrue(f.is_documentation_page()) + self.assertFalse(f.is_static_page()) + self.assertFalse(f.is_media_file()) + self.assertFalse(f.is_javascript()) + self.assertFalse(f.is_css()) + + def test_md_file_nested_use_directory_urls(self): + f = File('foo/bar.md', '/path/to/docs', '/path/to/site', use_directory_urls=True) + self.assertPathsEqual(f.src_path, 'foo/bar.md') + self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.md') + self.assertPathsEqual(f.dest_path, 'foo/bar/index.html') + self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar/index.html') + self.assertEqual(f.url, 'foo/bar/') + self.assertEqual(f.name, 'bar') + self.assertTrue(f.is_documentation_page()) + self.assertFalse(f.is_static_page()) + self.assertFalse(f.is_media_file()) + self.assertFalse(f.is_javascript()) + self.assertFalse(f.is_css()) + + def test_md_index_file(self): + f = File('index.md', '/path/to/docs', '/path/to/site', use_directory_urls=False) + self.assertPathsEqual(f.src_path, 'index.md') + self.assertPathsEqual(f.abs_src_path, '/path/to/docs/index.md') + self.assertPathsEqual(f.dest_path, 'index.html') + self.assertPathsEqual(f.abs_dest_path, '/path/to/site/index.html') + self.assertEqual(f.url, 'index.html') + self.assertEqual(f.name, 'index') + self.assertTrue(f.is_documentation_page()) + self.assertFalse(f.is_static_page()) + self.assertFalse(f.is_media_file()) + self.assertFalse(f.is_javascript()) + self.assertFalse(f.is_css()) + + def test_md_index_file_use_directory_urls(self): + f = File('index.md', '/path/to/docs', '/path/to/site', use_directory_urls=True) + self.assertPathsEqual(f.src_path, 'index.md') + self.assertPathsEqual(f.abs_src_path, '/path/to/docs/index.md') + self.assertPathsEqual(f.dest_path, 'index.html') + self.assertPathsEqual(f.abs_dest_path, '/path/to/site/index.html') + self.assertEqual(f.url, '.') + self.assertEqual(f.name, 'index') + self.assertTrue(f.is_documentation_page()) + self.assertFalse(f.is_static_page()) + self.assertFalse(f.is_media_file()) + self.assertFalse(f.is_javascript()) + self.assertFalse(f.is_css()) + + def test_md_index_file_nested(self): + f = File('foo/index.md', '/path/to/docs', '/path/to/site', use_directory_urls=False) + self.assertPathsEqual(f.src_path, 'foo/index.md') + self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/index.md') + self.assertPathsEqual(f.dest_path, 'foo/index.html') + self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/index.html') + self.assertEqual(f.url, 'foo/index.html') + self.assertEqual(f.name, 'index') + self.assertTrue(f.is_documentation_page()) + self.assertFalse(f.is_static_page()) + self.assertFalse(f.is_media_file()) + self.assertFalse(f.is_javascript()) + self.assertFalse(f.is_css()) + + def test_md_index_file_nested_use_directory_urls(self): + f = File('foo/index.md', '/path/to/docs', '/path/to/site', use_directory_urls=True) + self.assertPathsEqual(f.src_path, 'foo/index.md') + self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/index.md') + self.assertPathsEqual(f.dest_path, 'foo/index.html') + self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/index.html') + self.assertEqual(f.url, 'foo/') + self.assertEqual(f.name, 'index') + self.assertTrue(f.is_documentation_page()) + self.assertFalse(f.is_static_page()) + self.assertFalse(f.is_media_file()) + self.assertFalse(f.is_javascript()) + self.assertFalse(f.is_css()) + + def test_static_file(self): + f = File('foo/bar.html', '/path/to/docs', '/path/to/site', use_directory_urls=False) + self.assertPathsEqual(f.src_path, 'foo/bar.html') + self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.html') + self.assertPathsEqual(f.dest_path, 'foo/bar.html') + self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.html') + self.assertEqual(f.url, 'foo/bar.html') + self.assertEqual(f.name, 'bar') + self.assertFalse(f.is_documentation_page()) + self.assertTrue(f.is_static_page()) + self.assertFalse(f.is_media_file()) + self.assertFalse(f.is_javascript()) + self.assertFalse(f.is_css()) + + def test_static_file_use_directory_urls(self): + f = File('foo/bar.html', '/path/to/docs', '/path/to/site', use_directory_urls=True) + self.assertPathsEqual(f.src_path, 'foo/bar.html') + self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.html') + self.assertPathsEqual(f.dest_path, 'foo/bar.html') + self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.html') + self.assertEqual(f.url, 'foo/bar.html') + self.assertEqual(f.name, 'bar') + self.assertFalse(f.is_documentation_page()) + self.assertTrue(f.is_static_page()) + self.assertFalse(f.is_media_file()) + self.assertFalse(f.is_javascript()) + self.assertFalse(f.is_css()) + + def test_media_file(self): + f = File('foo/bar.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=False) + self.assertPathsEqual(f.src_path, 'foo/bar.jpg') + self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.jpg') + self.assertPathsEqual(f.dest_path, 'foo/bar.jpg') + self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.jpg') + self.assertEqual(f.url, 'foo/bar.jpg') + self.assertEqual(f.name, 'bar') + self.assertFalse(f.is_documentation_page()) + self.assertFalse(f.is_static_page()) + self.assertTrue(f.is_media_file()) + self.assertFalse(f.is_javascript()) + self.assertFalse(f.is_css()) + + def test_media_file_use_directory_urls(self): + f = File('foo/bar.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=True) + self.assertPathsEqual(f.src_path, 'foo/bar.jpg') + self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.jpg') + self.assertPathsEqual(f.dest_path, 'foo/bar.jpg') + self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.jpg') + self.assertEqual(f.url, 'foo/bar.jpg') + self.assertEqual(f.name, 'bar') + self.assertFalse(f.is_documentation_page()) + self.assertFalse(f.is_static_page()) + self.assertTrue(f.is_media_file()) + self.assertFalse(f.is_javascript()) + self.assertFalse(f.is_css()) + + def test_javascript_file(self): + f = File('foo/bar.js', '/path/to/docs', '/path/to/site', use_directory_urls=False) + self.assertPathsEqual(f.src_path, 'foo/bar.js') + self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.js') + self.assertPathsEqual(f.dest_path, 'foo/bar.js') + self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.js') + self.assertEqual(f.url, 'foo/bar.js') + self.assertEqual(f.name, 'bar') + self.assertFalse(f.is_documentation_page()) + self.assertFalse(f.is_static_page()) + self.assertTrue(f.is_media_file()) + self.assertTrue(f.is_javascript()) + self.assertFalse(f.is_css()) + + def test_javascript_file_use_directory_urls(self): + f = File('foo/bar.js', '/path/to/docs', '/path/to/site', use_directory_urls=True) + self.assertPathsEqual(f.src_path, 'foo/bar.js') + self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.js') + self.assertPathsEqual(f.dest_path, 'foo/bar.js') + self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.js') + self.assertEqual(f.url, 'foo/bar.js') + self.assertEqual(f.name, 'bar') + self.assertFalse(f.is_documentation_page()) + self.assertFalse(f.is_static_page()) + self.assertTrue(f.is_media_file()) + self.assertTrue(f.is_javascript()) + self.assertFalse(f.is_css()) + + def test_css_file(self): + f = File('foo/bar.css', '/path/to/docs', '/path/to/site', use_directory_urls=False) + self.assertPathsEqual(f.src_path, 'foo/bar.css') + self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.css') + self.assertPathsEqual(f.dest_path, 'foo/bar.css') + self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.css') + self.assertEqual(f.url, 'foo/bar.css') + self.assertEqual(f.name, 'bar') + self.assertFalse(f.is_documentation_page()) + self.assertFalse(f.is_static_page()) + self.assertTrue(f.is_media_file()) + self.assertFalse(f.is_javascript()) + self.assertTrue(f.is_css()) + + def test_css_file_use_directory_urls(self): + f = File('foo/bar.css', '/path/to/docs', '/path/to/site', use_directory_urls=True) + self.assertPathsEqual(f.src_path, 'foo/bar.css') + self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.css') + self.assertPathsEqual(f.dest_path, 'foo/bar.css') + self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.css') + self.assertEqual(f.url, 'foo/bar.css') + self.assertEqual(f.name, 'bar') + self.assertFalse(f.is_documentation_page()) + self.assertFalse(f.is_static_page()) + self.assertTrue(f.is_media_file()) + self.assertFalse(f.is_javascript()) + self.assertTrue(f.is_css()) + + def test_files(self): + fs = [ + File('index.md', '/path/to/docs', '/path/to/site', use_directory_urls=True), + File('foo/bar.md', '/path/to/docs', '/path/to/site', use_directory_urls=True), + File('foo/bar.html', '/path/to/docs', '/path/to/site', use_directory_urls=True), + File('foo/bar.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=True), + File('foo/bar.js', '/path/to/docs', '/path/to/site', use_directory_urls=True), + File('foo/bar.css', '/path/to/docs', '/path/to/site', use_directory_urls=True) + ] + files = Files(fs) + self.assertEqual([f for f in files], fs) + self.assertEqual(len(files), 6) + self.assertEqual(files.documentation_pages(), [fs[0], fs[1]]) + self.assertEqual(files.static_pages(), [fs[2]]) + self.assertEqual(files.media_files(), [fs[3], fs[4], fs[5]]) + self.assertEqual(files.javascript_files(), [fs[4]]) + self.assertEqual(files.css_files(), [fs[5]]) + self.assertEqual(files.get_file_from_path('foo/bar.jpg'), fs[3]) + self.assertEqual(files.get_file_from_path('foo/bar.jpg'), fs[3]) + self.assertEqual(files.get_file_from_path('missing.jpg'), None) + self.assertTrue(fs[2].src_path in files) + self.assertTrue(fs[2].src_path in files) + extra_file = File('extra.md', '/path/to/docs', '/path/to/site', use_directory_urls=True) + self.assertFalse(extra_file.src_path in files) + files.append(extra_file) + self.assertEqual(len(files), 7) + self.assertTrue(extra_file.src_path in files) + self.assertEqual(files.documentation_pages(), [fs[0], fs[1], extra_file]) + + def test_filter_paths(self): + # Root level file + self.assertFalse(_filter_paths('foo.md', 'foo.md', False, ['bar.md'])) + self.assertTrue(_filter_paths('foo.md', 'foo.md', False, ['foo.md'])) + + # Nested file + self.assertFalse(_filter_paths('foo.md', 'baz/foo.md', False, ['bar.md'])) + self.assertTrue(_filter_paths('foo.md', 'baz/foo.md', False, ['foo.md'])) + + # Wildcard + self.assertFalse(_filter_paths('foo.md', 'foo.md', False, ['*.txt'])) + self.assertTrue(_filter_paths('foo.md', 'foo.md', False, ['*.md'])) + + # Root level dir + self.assertFalse(_filter_paths('bar', 'bar', True, ['/baz'])) + self.assertFalse(_filter_paths('bar', 'bar', True, ['/baz/'])) + self.assertTrue(_filter_paths('bar', 'bar', True, ['/bar'])) + self.assertTrue(_filter_paths('bar', 'bar', True, ['/bar/'])) + + # Nested dir + self.assertFalse(_filter_paths('bar', 'foo/bar', True, ['/bar'])) + self.assertFalse(_filter_paths('bar', 'foo/bar', True, ['/bar/'])) + self.assertTrue(_filter_paths('bar', 'foo/bar', True, ['bar/'])) + + # Files that look like dirs (no extension). Note that `is_dir` is `False`. + self.assertFalse(_filter_paths('bar', 'bar', False, ['bar/'])) + self.assertFalse(_filter_paths('bar', 'foo/bar', False, ['bar/'])) + + def test_get_relative_url_use_directory_urls(self): + to_files = [ + 'index.md', + 'foo/index.md', + 'foo/bar/index.md', + 'foo/bar/baz/index.md', + 'foo.md', + 'foo/bar.md', + 'foo/bar/baz.md' + ] + + to_file_urls = [ + '.', + 'foo/', + 'foo/bar/', + 'foo/bar/baz/', + 'foo/', + 'foo/bar/', + 'foo/bar/baz/' + ] + + from_file = File('img.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=True) + expected = [ + 'img.jpg', # img.jpg relative to . + '../img.jpg', # img.jpg relative to foo/ + '../../img.jpg', # img.jpg relative to foo/bar/ + '../../../img.jpg', # img.jpg relative to foo/bar/baz/ + '../img.jpg', # img.jpg relative to foo + '../../img.jpg', # img.jpg relative to foo/bar + '../../../img.jpg' # img.jpg relative to foo/bar/baz + ] + + for i, filename in enumerate(to_files): + file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=True) + self.assertEqual(from_file.url, 'img.jpg') + self.assertEqual(file.url, to_file_urls[i]) + self.assertEqual(from_file.url_relative_to(file.url), expected[i]) + self.assertEqual(from_file.url_relative_to(file), expected[i]) + + from_file = File('foo/img.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=True) + expected = [ + 'foo/img.jpg', # foo/img.jpg relative to . + 'img.jpg', # foo/img.jpg relative to foo/ + '../img.jpg', # foo/img.jpg relative to foo/bar/ + '../../img.jpg', # foo/img.jpg relative to foo/bar/baz/ + 'img.jpg', # foo/img.jpg relative to foo + '../img.jpg', # foo/img.jpg relative to foo/bar + '../../img.jpg' # foo/img.jpg relative to foo/bar/baz + ] + + for i, filename in enumerate(to_files): + file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=True) + self.assertEqual(from_file.url, 'foo/img.jpg') + self.assertEqual(file.url, to_file_urls[i]) + self.assertEqual(from_file.url_relative_to(file.url), expected[i]) + self.assertEqual(from_file.url_relative_to(file), expected[i]) + + from_file = File('index.html', '/path/to/docs', '/path/to/site', use_directory_urls=True) + expected = [ + '.', # . relative to . + '..', # . relative to foo/ + '../..', # . relative to foo/bar/ + '../../..', # . relative to foo/bar/baz/ + '..', # . relative to foo + '../..', # . relative to foo/bar + '../../..' # . relative to foo/bar/baz + ] + + for i, filename in enumerate(to_files): + file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=True) + self.assertEqual(from_file.url, '.') + self.assertEqual(file.url, to_file_urls[i]) + self.assertEqual(from_file.url_relative_to(file.url), expected[i]) + self.assertEqual(from_file.url_relative_to(file), expected[i]) + + from_file = File('file.md', '/path/to/docs', '/path/to/site', use_directory_urls=True) + expected = [ + 'file/', # file relative to . + '../file/', # file relative to foo/ + '../../file/', # file relative to foo/bar/ + '../../../file/', # file relative to foo/bar/baz/ + '../file/', # file relative to foo + '../../file/', # file relative to foo/bar + '../../../file/' # file relative to foo/bar/baz + ] + + for i, filename in enumerate(to_files): + file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=True) + self.assertEqual(from_file.url, 'file/') + self.assertEqual(file.url, to_file_urls[i]) + self.assertEqual(from_file.url_relative_to(file.url), expected[i]) + self.assertEqual(from_file.url_relative_to(file), expected[i]) + + def test_get_relative_url(self): + to_files = [ + 'index.md', + 'foo/index.md', + 'foo/bar/index.md', + 'foo/bar/baz/index.md', + 'foo.md', + 'foo/bar.md', + 'foo/bar/baz.md' + ] + + to_file_urls = [ + 'index.html', + 'foo/index.html', + 'foo/bar/index.html', + 'foo/bar/baz/index.html', + 'foo.html', + 'foo/bar.html', + 'foo/bar/baz.html' + ] + + from_file = File('img.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=False) + expected = [ + 'img.jpg', # img.jpg relative to . + '../img.jpg', # img.jpg relative to foo/ + '../../img.jpg', # img.jpg relative to foo/bar/ + '../../../img.jpg', # img.jpg relative to foo/bar/baz/ + 'img.jpg', # img.jpg relative to foo.html + '../img.jpg', # img.jpg relative to foo/bar.html + '../../img.jpg' # img.jpg relative to foo/bar/baz.html + ] + + for i, filename in enumerate(to_files): + file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=False) + self.assertEqual(from_file.url, 'img.jpg') + self.assertEqual(file.url, to_file_urls[i]) + self.assertEqual(from_file.url_relative_to(file.url), expected[i]) + self.assertEqual(from_file.url_relative_to(file), expected[i]) + + from_file = File('foo/img.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=False) + expected = [ + 'foo/img.jpg', # foo/img.jpg relative to . + 'img.jpg', # foo/img.jpg relative to foo/ + '../img.jpg', # foo/img.jpg relative to foo/bar/ + '../../img.jpg', # foo/img.jpg relative to foo/bar/baz/ + 'foo/img.jpg', # foo/img.jpg relative to foo.html + 'img.jpg', # foo/img.jpg relative to foo/bar.html + '../img.jpg' # foo/img.jpg relative to foo/bar/baz.html + ] + + for i, filename in enumerate(to_files): + file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=False) + self.assertEqual(from_file.url, 'foo/img.jpg') + self.assertEqual(file.url, to_file_urls[i]) + self.assertEqual(from_file.url_relative_to(file.url), expected[i]) + self.assertEqual(from_file.url_relative_to(file), expected[i]) + + from_file = File('index.html', '/path/to/docs', '/path/to/site', use_directory_urls=False) + expected = [ + 'index.html', # index.html relative to . + '../index.html', # index.html relative to foo/ + '../../index.html', # index.html relative to foo/bar/ + '../../../index.html', # index.html relative to foo/bar/baz/ + 'index.html', # index.html relative to foo.html + '../index.html', # index.html relative to foo/bar.html + '../../index.html' # index.html relative to foo/bar/baz.html + ] + + for i, filename in enumerate(to_files): + file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=False) + self.assertEqual(from_file.url, 'index.html') + self.assertEqual(file.url, to_file_urls[i]) + self.assertEqual(from_file.url_relative_to(file.url), expected[i]) + self.assertEqual(from_file.url_relative_to(file), expected[i]) + + from_file = File('file.html', '/path/to/docs', '/path/to/site', use_directory_urls=False) + expected = [ + 'file.html', # file.html relative to . + '../file.html', # file.html relative to foo/ + '../../file.html', # file.html relative to foo/bar/ + '../../../file.html', # file.html relative to foo/bar/baz/ + 'file.html', # file.html relative to foo.html + '../file.html', # file.html relative to foo/bar.html + '../../file.html' # file.html relative to foo/bar/baz.html + ] + + for i, filename in enumerate(to_files): + file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=False) + self.assertEqual(from_file.url, 'file.html') + self.assertEqual(file.url, to_file_urls[i]) + self.assertEqual(from_file.url_relative_to(file.url), expected[i]) + self.assertEqual(from_file.url_relative_to(file), expected[i]) + + @tempdir(files=[ + 'index.md', + 'bar.css', + 'bar.html', + 'bar.jpg', + 'bar.js', + 'bar.md', + '.dotfile', + 'templates/foo.html' + ]) + def test_get_files(self, tdir): + config = load_config(docs_dir=tdir, extra_css=['bar.css'], extra_javascript=['bar.js']) + files = get_files(config) + expected = ['index.md', 'bar.css', 'bar.html', 'bar.jpg', 'bar.js', 'bar.md'] + self.assertIsInstance(files, Files) + self.assertEqual(len(files), len(expected)) + self.assertEqual([f.src_path for f in files], expected) + + @tempdir() + @tempdir(files={'test.txt': 'source content'}) + def test_copy_file(self, src_dir, dest_dir): + file = File('test.txt', src_dir, dest_dir, use_directory_urls=False) + dest_path = os.path.join(dest_dir, 'test.txt') + self.assertPathNotExists(dest_path) + file.copy_file() + self.assertPathIsFile(dest_path) + + @tempdir(files={'test.txt': 'destination content'}) + @tempdir(files={'test.txt': 'source content'}) + def test_copy_file_clean_modified(self, src_dir, dest_dir): + file = File('test.txt', src_dir, dest_dir, use_directory_urls=False) + file.is_modified = mock.Mock(return_value=True) + dest_path = os.path.join(dest_dir, 'test.txt') + file.copy_file(dirty=False) + self.assertPathIsFile(dest_path) + with io.open(dest_path, 'r', encoding='utf-8') as f: + self.assertEqual(f.read(), 'source content') + + @tempdir(files={'test.txt': 'destination content'}) + @tempdir(files={'test.txt': 'source content'}) + def test_copy_file_dirty_modified(self, src_dir, dest_dir): + file = File('test.txt', src_dir, dest_dir, use_directory_urls=False) + file.is_modified = mock.Mock(return_value=True) + dest_path = os.path.join(dest_dir, 'test.txt') + file.copy_file(dirty=True) + self.assertPathIsFile(dest_path) + with io.open(dest_path, 'r', encoding='utf-8') as f: + self.assertEqual(f.read(), 'source content') + + @tempdir(files={'test.txt': 'destination content'}) + @tempdir(files={'test.txt': 'source content'}) + def test_copy_file_dirty_not_modified(self, src_dir, dest_dir): + file = File('test.txt', src_dir, dest_dir, use_directory_urls=False) + file.is_modified = mock.Mock(return_value=False) + dest_path = os.path.join(dest_dir, 'test.txt') + file.copy_file(dirty=True) + self.assertPathIsFile(dest_path) + with io.open(dest_path, 'r', encoding='utf-8') as f: + self.assertEqual(f.read(), 'destination content') diff --git a/mkdocs/tests/structure/nav_tests.py b/mkdocs/tests/structure/nav_tests.py new file mode 100644 index 0000000000..3596fb6f85 --- /dev/null +++ b/mkdocs/tests/structure/nav_tests.py @@ -0,0 +1,343 @@ +#!/usr/bin/env python +# coding: utf-8 + +from __future__ import unicode_literals +import sys +import unittest + +from mkdocs.structure.nav import get_navigation +from mkdocs.structure.files import File, Files +from mkdocs.structure.pages import Page +from mkdocs.tests.base import dedent, load_config + + +class SiteNavigationTests(unittest.TestCase): + + maxDiff = None + + def test_simple_nav(self): + nav_cfg = [ + {'Home': 'index.md'}, + {'About': 'about.md'} + ] + expected = dedent(""" + Page(title='Home', url='/') + Page(title='About', url='/about/') + """) + cfg = load_config(nav=nav_cfg, site_url='http://example.com/') + files = Files( + [File(list(item.values())[0], cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + for item in nav_cfg] + ) + site_navigation = get_navigation(files, cfg) + self.assertEqual(str(site_navigation).strip(), expected) + self.assertEqual(len(site_navigation.items), 2) + self.assertEqual(len(site_navigation.pages), 2) + self.assertEqual(repr(site_navigation.homepage), "Page(title='Home', url='/')") + + def test_nav_no_directory_urls(self): + nav_cfg = [ + {'Home': 'index.md'}, + {'About': 'about.md'} + ] + expected = dedent(""" + Page(title='Home', url='/index.html') + Page(title='About', url='/about.html') + """) + cfg = load_config(nav=nav_cfg, use_directory_urls=False, site_url='http://example.com/') + files = Files( + [File(list(item.values())[0], cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + for item in nav_cfg] + ) + site_navigation = get_navigation(files, cfg) + self.assertEqual(str(site_navigation).strip(), expected) + self.assertEqual(len(site_navigation.items), 2) + self.assertEqual(len(site_navigation.pages), 2) + + def test_nav_missing_page(self): + nav_cfg = [ + {'Home': 'index.md'} + ] + expected = dedent(""" + Page(title='Home', url='/') + """) + cfg = load_config(nav=nav_cfg, site_url='http://example.com/') + files = Files([ + File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('page_not_in_nav.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + ]) + site_navigation = get_navigation(files, cfg) + self.assertEqual(str(site_navigation).strip(), expected) + self.assertEqual(len(site_navigation.items), 1) + self.assertEqual(len(site_navigation.pages), 1) + for file in files: + self.assertIsInstance(file.page, Page) + + def test_nav_no_title(self): + nav_cfg = [ + 'index.md', + {'About': 'about.md'} + ] + expected = dedent(""" + Page(title=[blank], url='/') + Page(title='About', url='/about/') + """) + cfg = load_config(nav=nav_cfg, site_url='http://example.com/') + files = Files([ + File(nav_cfg[0], cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File(nav_cfg[1]['About'], cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + ]) + site_navigation = get_navigation(files, cfg) + self.assertEqual(str(site_navigation).strip(), expected) + self.assertEqual(len(site_navigation.items), 2) + self.assertEqual(len(site_navigation.pages), 2) + + def test_nav_external_links(self): + nav_cfg = [ + {'Home': 'index.md'}, + {'Local': '/local.html'}, + {'External': 'http://example.com/external.html'} + ] + expected = dedent(""" + Page(title='Home', url='/') + Link(title='Local', url='/local.html') + Link(title='External', url='http://example.com/external.html') + """) + cfg = load_config(nav=nav_cfg, site_url='http://example.com/') + files = Files([File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])]) + site_navigation = get_navigation(files, cfg) + self.assertEqual(str(site_navigation).strip(), expected) + self.assertEqual(len(site_navigation.items), 3) + self.assertEqual(len(site_navigation.pages), 1) + + def test_indented_nav(self): + nav_cfg = [ + {'Home': 'index.md'}, + {'API Guide': [ + {'Running': 'api-guide/running.md'}, + {'Testing': 'api-guide/testing.md'}, + {'Debugging': 'api-guide/debugging.md'}, + {'Advanced': [ + {'Part 1': 'api-guide/advanced/part-1.md'}, + ]}, + ]}, + {'About': [ + {'Release notes': 'about/release-notes.md'}, + {'License': '/license.html'} + ]}, + {'External': 'https://example.com/'} + ] + expected = dedent(""" + Page(title='Home', url='/') + Section(title='API Guide') + Page(title='Running', url='/api-guide/running/') + Page(title='Testing', url='/api-guide/testing/') + Page(title='Debugging', url='/api-guide/debugging/') + Section(title='Advanced') + Page(title='Part 1', url='/api-guide/advanced/part-1/') + Section(title='About') + Page(title='Release notes', url='/about/release-notes/') + Link(title='License', url='/license.html') + Link(title='External', url='https://example.com/') + """) + cfg = load_config(nav=nav_cfg, site_url='http://example.com/') + files = Files([ + File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('api-guide/running.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('api-guide/testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('api-guide/debugging.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('api-guide/advanced/part-1.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('about/release-notes.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + ]) + site_navigation = get_navigation(files, cfg) + self.assertEqual(str(site_navigation).strip(), expected) + self.assertEqual(len(site_navigation.items), 4) + self.assertEqual(len(site_navigation.pages), 6) + self.assertEqual(repr(site_navigation.homepage), "Page(title='Home', url='/')") + self.assertIsNone(site_navigation.items[0].parent) + self.assertEqual(site_navigation.items[0].ancestors, []) + self.assertIsNone(site_navigation.items[1].parent) + self.assertEqual(site_navigation.items[1].ancestors, []) + self.assertEqual(len(site_navigation.items[1].children), 4) + self.assertEqual(repr(site_navigation.items[1].children[0].parent), "Section(title='API Guide')") + self.assertEqual(site_navigation.items[1].children[0].ancestors, [site_navigation.items[1]]) + self.assertEqual(repr(site_navigation.items[1].children[1].parent), "Section(title='API Guide')") + self.assertEqual(site_navigation.items[1].children[1].ancestors, [site_navigation.items[1]]) + self.assertEqual(repr(site_navigation.items[1].children[2].parent), "Section(title='API Guide')") + self.assertEqual(site_navigation.items[1].children[2].ancestors, [site_navigation.items[1]]) + self.assertEqual(repr(site_navigation.items[1].children[3].parent), "Section(title='API Guide')") + self.assertEqual(site_navigation.items[1].children[3].ancestors, [site_navigation.items[1]]) + self.assertEqual(len(site_navigation.items[1].children[3].children), 1) + self.assertEqual(repr(site_navigation.items[1].children[3].children[0].parent), "Section(title='Advanced')") + self.assertEqual(site_navigation.items[1].children[3].children[0].ancestors, + [site_navigation.items[1].children[3], site_navigation.items[1]]) + self.assertIsNone(site_navigation.items[2].parent) + self.assertEqual(len(site_navigation.items[2].children), 2) + self.assertEqual(repr(site_navigation.items[2].children[0].parent), "Section(title='About')") + self.assertEqual(site_navigation.items[2].children[0].ancestors, [site_navigation.items[2]]) + self.assertEqual(repr(site_navigation.items[2].children[1].parent), "Section(title='About')") + self.assertEqual(site_navigation.items[2].children[1].ancestors, [site_navigation.items[2]]) + self.assertIsNone(site_navigation.items[3].parent) + self.assertEqual(site_navigation.items[3].ancestors, []) + self.assertIsNone(site_navigation.items[3].children) + + def test_nested_ungrouped_nav(self): + nav_cfg = [ + {'Home': 'index.md'}, + {'Contact': 'about/contact.md'}, + {'License Title': 'about/sub/license.md'}, + ] + expected = dedent(""" + Page(title='Home', url='/') + Page(title='Contact', url='/about/contact/') + Page(title='License Title', url='/about/sub/license/') + """) + cfg = load_config(nav=nav_cfg, site_url='http://example.com/') + files = Files( + [File(list(item.values())[0], cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + for item in nav_cfg] + ) + site_navigation = get_navigation(files, cfg) + self.assertEqual(str(site_navigation).strip(), expected) + self.assertEqual(len(site_navigation.items), 3) + self.assertEqual(len(site_navigation.pages), 3) + + def test_nested_ungrouped_nav_no_titles(self): + nav_cfg = [ + 'index.md', + 'about/contact.md', + 'about/sub/license.md' + ] + expected = dedent(""" + Page(title=[blank], url='/') + Page(title=[blank], url='/about/contact/') + Page(title=[blank], url='/about/sub/license/') + """) + + cfg = load_config(nav=nav_cfg, site_url='http://example.com/') + files = Files( + [File(item, cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) for item in nav_cfg] + ) + site_navigation = get_navigation(files, cfg) + self.assertEqual(str(site_navigation).strip(), expected) + self.assertEqual(len(site_navigation.items), 3) + self.assertEqual(len(site_navigation.pages), 3) + self.assertEqual(repr(site_navigation.homepage), "Page(title=[blank], url='/')") + + @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") + def test_nested_ungrouped_no_titles_windows(self): + nav_cfg = [ + 'index.md', + 'about\\contact.md', + 'about\\sub\\license.md', + ] + expected = dedent(""" + Page(title=[blank], url='/') + Page(title=[blank], url='/about/contact/') + Page(title=[blank], url='/about/sub/license/') + """) + + cfg = load_config(nav=nav_cfg, site_url='http://example.com/') + files = Files( + [File(item, cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) for item in nav_cfg] + ) + site_navigation = get_navigation(files, cfg) + self.assertEqual(str(site_navigation).strip(), expected) + self.assertEqual(len(site_navigation.items), 3) + self.assertEqual(len(site_navigation.pages), 3) + + def test_nav_from_files(self): + expected = dedent(""" + Page(title=[blank], url='/') + Page(title=[blank], url='/about/') + """) + cfg = load_config(site_url='http://example.com/') + files = Files([ + File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('about.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + ]) + site_navigation = get_navigation(files, cfg) + self.assertEqual(str(site_navigation).strip(), expected) + self.assertEqual(len(site_navigation.items), 2) + self.assertEqual(len(site_navigation.pages), 2) + self.assertEqual(repr(site_navigation.homepage), "Page(title=[blank], url='/')") + + def test_nav_from_nested_files(self): + expected = dedent(""" + Page(title=[blank], url='/') + Section(title='About') + Page(title=[blank], url='/about/license/') + Page(title=[blank], url='/about/release-notes/') + Section(title='Api guide') + Page(title=[blank], url='/api-guide/debugging/') + Page(title=[blank], url='/api-guide/running/') + Page(title=[blank], url='/api-guide/testing/') + Section(title='Advanced') + Page(title=[blank], url='/api-guide/advanced/part-1/') + """) + cfg = load_config(site_url='http://example.com/') + files = Files([ + File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('about/license.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('about/release-notes.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('api-guide/debugging.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('api-guide/running.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('api-guide/testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('api-guide/advanced/part-1.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + ]) + site_navigation = get_navigation(files, cfg) + self.assertEqual(str(site_navigation).strip(), expected) + self.assertEqual(len(site_navigation.items), 3) + self.assertEqual(len(site_navigation.pages), 7) + self.assertEqual(repr(site_navigation.homepage), "Page(title=[blank], url='/')") + + def test_active(self): + nav_cfg = [ + {'Home': 'index.md'}, + {'API Guide': [ + {'Running': 'api-guide/running.md'}, + {'Testing': 'api-guide/testing.md'}, + {'Debugging': 'api-guide/debugging.md'}, + {'Advanced': [ + {'Part 1': 'api-guide/advanced/part-1.md'}, + ]}, + ]}, + {'About': [ + {'Release notes': 'about/release-notes.md'}, + {'License': 'about/license.md'} + ]} + ] + cfg = load_config(nav=nav_cfg, site_url='http://example.com/') + files = Files([ + File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('api-guide/running.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('api-guide/testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('api-guide/debugging.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('api-guide/advanced/part-1.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('about/release-notes.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + File('about/license.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), + ]) + site_navigation = get_navigation(files, cfg) + # Confirm nothing is active + self.assertTrue(all(page.active is False for page in site_navigation.pages)) + self.assertTrue(all(item.active is False for item in site_navigation.items)) + # Activate + site_navigation.items[1].children[3].children[0].active = True + # Confirm ancestors are activated + self.assertTrue(site_navigation.items[1].children[3].children[0].active) + self.assertTrue(site_navigation.items[1].children[3].active) + self.assertTrue(site_navigation.items[1].active) + # Confirm non-ancestors are not activated + self.assertFalse(site_navigation.items[0].active) + self.assertFalse(site_navigation.items[1].children[0].active) + self.assertFalse(site_navigation.items[1].children[1].active) + self.assertFalse(site_navigation.items[1].children[2].active) + self.assertFalse(site_navigation.items[2].active) + self.assertFalse(site_navigation.items[2].children[0].active) + self.assertFalse(site_navigation.items[2].children[1].active) + # Deactivate + site_navigation.items[1].children[3].children[0].active = False + # Confirm ancestors are deactivated + self.assertFalse(site_navigation.items[1].children[3].children[0].active) + self.assertFalse(site_navigation.items[1].children[3].active) + self.assertFalse(site_navigation.items[1].active) diff --git a/mkdocs/tests/structure/page_tests.py b/mkdocs/tests/structure/page_tests.py new file mode 100644 index 0000000000..1557badbc8 --- /dev/null +++ b/mkdocs/tests/structure/page_tests.py @@ -0,0 +1,789 @@ +from __future__ import unicode_literals + +import unittest +import os +import sys +import mock +import io + +try: + # py>=3.2 + from tempfile import TemporaryDirectory +except ImportError: + from backports.tempfile import TemporaryDirectory + +from mkdocs.structure.pages import Page +from mkdocs.structure.files import File, Files +from mkdocs.tests.base import load_config, dedent, LogTestCase +from mkdocs.exceptions import MarkdownNotFound + + +class PageTests(unittest.TestCase): + + DOCS_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../integration/subpages/docs') + + def test_homepage(self): + cfg = load_config(docs_dir=self.DOCS_DIR) + fl = File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + self.assertIsNone(fl.page) + pg = Page('Foo', fl, cfg) + self.assertEqual(fl.page, pg) + self.assertEqual(pg.url, '') + self.assertEqual(pg.abs_url, None) + self.assertEqual(pg.canonical_url, None) + self.assertEqual(pg.edit_url, None) + self.assertEqual(pg.file, fl) + self.assertEqual(pg.content, None) + self.assertTrue(pg.is_homepage) + self.assertTrue(pg.is_index) + self.assertTrue(pg.is_page) + self.assertFalse(pg.is_section) + self.assertTrue(pg.is_top_level) + self.assertEqual(pg.markdown, None) + self.assertEqual(pg.meta, {}) + self.assertEqual(pg.next_page, None) + self.assertEqual(pg.parent, None) + self.assertEqual(pg.previous_page, None) + self.assertEqual(pg.title, 'Foo') + self.assertEqual(pg.toc, []) + + def test_nested_index_page(self): + cfg = load_config(docs_dir=self.DOCS_DIR) + fl = File('sub1/index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + pg = Page('Foo', fl, cfg) + pg.parent = 'foo' + self.assertEqual(pg.url, 'sub1/') + self.assertEqual(pg.abs_url, None) + self.assertEqual(pg.canonical_url, None) + self.assertEqual(pg.edit_url, None) + self.assertEqual(pg.file, fl) + self.assertEqual(pg.content, None) + self.assertFalse(pg.is_homepage) + self.assertTrue(pg.is_index) + self.assertTrue(pg.is_page) + self.assertFalse(pg.is_section) + self.assertFalse(pg.is_top_level) + self.assertEqual(pg.markdown, None) + self.assertEqual(pg.meta, {}) + self.assertEqual(pg.next_page, None) + self.assertEqual(pg.parent, 'foo') + self.assertEqual(pg.previous_page, None) + self.assertEqual(pg.title, 'Foo') + self.assertEqual(pg.toc, []) + + def test_nested_nonindex_page(self): + cfg = load_config(docs_dir=self.DOCS_DIR) + fl = File('sub1/non-index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + pg = Page('Foo', fl, cfg) + pg.parent = 'foo' + self.assertEqual(pg.url, 'sub1/non-index/') + self.assertEqual(pg.abs_url, None) + self.assertEqual(pg.canonical_url, None) + self.assertEqual(pg.edit_url, None) + self.assertEqual(pg.file, fl) + self.assertEqual(pg.content, None) + self.assertFalse(pg.is_homepage) + self.assertFalse(pg.is_index) + self.assertTrue(pg.is_page) + self.assertFalse(pg.is_section) + self.assertFalse(pg.is_top_level) + self.assertEqual(pg.markdown, None) + self.assertEqual(pg.meta, {}) + self.assertEqual(pg.next_page, None) + self.assertEqual(pg.parent, 'foo') + self.assertEqual(pg.previous_page, None) + self.assertEqual(pg.title, 'Foo') + self.assertEqual(pg.toc, []) + + def test_page_defaults(self): + cfg = load_config() + fl = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + pg = Page('Foo', fl, cfg) + self.assertRegexpMatches(pg.update_date, r'\d{4}-\d{2}-\d{2}') + self.assertEqual(pg.url, 'testing/') + self.assertEqual(pg.abs_url, None) + self.assertEqual(pg.canonical_url, None) + self.assertEqual(pg.edit_url, None) + self.assertEqual(pg.file, fl) + self.assertEqual(pg.content, None) + self.assertFalse(pg.is_homepage) + self.assertFalse(pg.is_index) + self.assertTrue(pg.is_page) + self.assertFalse(pg.is_section) + self.assertTrue(pg.is_top_level) + self.assertEqual(pg.markdown, None) + self.assertEqual(pg.meta, {}) + self.assertEqual(pg.next_page, None) + self.assertEqual(pg.parent, None) + self.assertEqual(pg.previous_page, None) + self.assertEqual(pg.title, 'Foo') + self.assertEqual(pg.toc, []) + + def test_page_no_directory_url(self): + cfg = load_config(use_directory_urls=False) + fl = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + pg = Page('Foo', fl, cfg) + self.assertEqual(pg.url, 'testing.html') + self.assertEqual(pg.abs_url, None) + self.assertEqual(pg.canonical_url, None) + self.assertEqual(pg.edit_url, None) + self.assertEqual(pg.file, fl) + self.assertEqual(pg.content, None) + self.assertFalse(pg.is_homepage) + self.assertFalse(pg.is_index) + self.assertTrue(pg.is_page) + self.assertFalse(pg.is_section) + self.assertTrue(pg.is_top_level) + self.assertEqual(pg.markdown, None) + self.assertEqual(pg.meta, {}) + self.assertEqual(pg.next_page, None) + self.assertEqual(pg.parent, None) + self.assertEqual(pg.previous_page, None) + self.assertEqual(pg.title, 'Foo') + self.assertEqual(pg.toc, []) + + def test_page_canonical_url(self): + cfg = load_config(site_url='http://example.com') + fl = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + pg = Page('Foo', fl, cfg) + self.assertEqual(pg.url, 'testing/') + self.assertEqual(pg.abs_url, '/testing/') + self.assertEqual(pg.canonical_url, 'http://example.com/testing/') + self.assertEqual(pg.edit_url, None) + self.assertEqual(pg.file, fl) + self.assertEqual(pg.content, None) + self.assertFalse(pg.is_homepage) + self.assertFalse(pg.is_index) + self.assertTrue(pg.is_page) + self.assertFalse(pg.is_section) + self.assertTrue(pg.is_top_level) + self.assertEqual(pg.markdown, None) + self.assertEqual(pg.meta, {}) + self.assertEqual(pg.next_page, None) + self.assertEqual(pg.parent, None) + self.assertEqual(pg.previous_page, None) + self.assertEqual(pg.title, 'Foo') + self.assertEqual(pg.toc, []) + + def test_page_canonical_url_nested(self): + cfg = load_config(site_url='http://example.com/foo/') + fl = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + pg = Page('Foo', fl, cfg) + self.assertEqual(pg.url, 'testing/') + self.assertEqual(pg.abs_url, '/foo/testing/') + self.assertEqual(pg.canonical_url, 'http://example.com/foo/testing/') + self.assertEqual(pg.edit_url, None) + self.assertEqual(pg.file, fl) + self.assertEqual(pg.content, None) + self.assertFalse(pg.is_homepage) + self.assertFalse(pg.is_index) + self.assertTrue(pg.is_page) + self.assertFalse(pg.is_section) + self.assertTrue(pg.is_top_level) + self.assertEqual(pg.markdown, None) + self.assertEqual(pg.meta, {}) + self.assertEqual(pg.next_page, None) + self.assertEqual(pg.parent, None) + self.assertEqual(pg.previous_page, None) + self.assertEqual(pg.title, 'Foo') + self.assertEqual(pg.toc, []) + + def test_page_canonical_url_nested_no_slash(self): + cfg = load_config(site_url='http://example.com/foo') + fl = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + pg = Page('Foo', fl, cfg) + self.assertEqual(pg.url, 'testing/') + self.assertEqual(pg.abs_url, '/foo/testing/') + self.assertEqual(pg.canonical_url, 'http://example.com/foo/testing/') + self.assertEqual(pg.edit_url, None) + self.assertEqual(pg.file, fl) + self.assertEqual(pg.content, None) + self.assertFalse(pg.is_homepage) + self.assertFalse(pg.is_index) + self.assertTrue(pg.is_page) + self.assertFalse(pg.is_section) + self.assertTrue(pg.is_top_level) + self.assertEqual(pg.markdown, None) + self.assertEqual(pg.meta, {}) + self.assertEqual(pg.next_page, None) + self.assertEqual(pg.parent, None) + self.assertEqual(pg.previous_page, None) + self.assertEqual(pg.title, 'Foo') + self.assertEqual(pg.toc, []) + + def test_predefined_page_title(self): + cfg = load_config() + fl = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + pg = Page('Page Title', fl, cfg) + pg.read_source(cfg) + self.assertEqual(pg.url, 'testing/') + self.assertEqual(pg.abs_url, None) + self.assertEqual(pg.canonical_url, None) + self.assertEqual(pg.edit_url, None) + self.assertEqual(pg.file, fl) + self.assertEqual(pg.content, None) + self.assertFalse(pg.is_homepage) + self.assertFalse(pg.is_index) + self.assertTrue(pg.is_page) + self.assertFalse(pg.is_section) + self.assertTrue(pg.is_top_level) + self.assertTrue(pg.markdown.startswith('# Welcome to MkDocs\n')) + self.assertEqual(pg.meta, {}) + self.assertEqual(pg.next_page, None) + self.assertEqual(pg.parent, None) + self.assertEqual(pg.previous_page, None) + self.assertEqual(pg.title, 'Page Title') + self.assertEqual(pg.toc, []) + + def test_page_title_from_markdown(self): + cfg = load_config() + fl = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + pg = Page(None, fl, cfg) + pg.read_source(cfg) + self.assertEqual(pg.url, 'testing/') + self.assertEqual(pg.abs_url, None) + self.assertEqual(pg.canonical_url, None) + self.assertEqual(pg.edit_url, None) + self.assertEqual(pg.file, fl) + self.assertEqual(pg.content, None) + self.assertFalse(pg.is_homepage) + self.assertFalse(pg.is_index) + self.assertTrue(pg.is_page) + self.assertFalse(pg.is_section) + self.assertTrue(pg.is_top_level) + self.assertTrue(pg.markdown.startswith('# Welcome to MkDocs\n')) + self.assertEqual(pg.meta, {}) + self.assertEqual(pg.next_page, None) + self.assertEqual(pg.parent, None) + self.assertEqual(pg.previous_page, None) + self.assertEqual(pg.title, 'Welcome to MkDocs') + self.assertEqual(pg.toc, []) + + def test_page_title_from_meta(self): + cfg = load_config(docs_dir=self.DOCS_DIR) + fl = File('metadata.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + pg = Page(None, fl, cfg) + pg.read_source(cfg) + self.assertEqual(pg.url, 'metadata/') + self.assertEqual(pg.abs_url, None) + self.assertEqual(pg.canonical_url, None) + self.assertEqual(pg.edit_url, None) + self.assertEqual(pg.file, fl) + self.assertEqual(pg.content, None) + self.assertFalse(pg.is_homepage) + self.assertFalse(pg.is_index) + self.assertTrue(pg.is_page) + self.assertFalse(pg.is_section) + self.assertTrue(pg.is_top_level) + self.assertTrue(pg.markdown.startswith('# Welcome to MkDocs\n')) + self.assertEqual(pg.meta, {'title': 'A Page Title'}) + self.assertEqual(pg.next_page, None) + self.assertEqual(pg.parent, None) + self.assertEqual(pg.previous_page, None) + self.assertEqual(pg.title, 'A Page Title') + self.assertEqual(pg.toc, []) + + def test_page_title_from_filename(self): + cfg = load_config(docs_dir=self.DOCS_DIR) + fl = File('page-title.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + pg = Page(None, fl, cfg) + pg.read_source(cfg) + self.assertEqual(pg.url, 'page-title/') + self.assertEqual(pg.abs_url, None) + self.assertEqual(pg.canonical_url, None) + self.assertEqual(pg.edit_url, None) + self.assertEqual(pg.file, fl) + self.assertEqual(pg.content, None) + self.assertFalse(pg.is_homepage) + self.assertFalse(pg.is_index) + self.assertTrue(pg.is_page) + self.assertFalse(pg.is_section) + self.assertTrue(pg.is_top_level) + self.assertTrue(pg.markdown.startswith('Page content.\n')) + self.assertEqual(pg.meta, {}) + self.assertEqual(pg.next_page, None) + self.assertEqual(pg.parent, None) + self.assertEqual(pg.previous_page, None) + self.assertEqual(pg.title, 'Page title') + self.assertEqual(pg.toc, []) + + def test_page_title_from_capitalized_filename(self): + cfg = load_config(docs_dir=self.DOCS_DIR) + fl = File('pageTitle.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + pg = Page(None, fl, cfg) + pg.read_source(cfg) + self.assertEqual(pg.url, 'pageTitle/') + self.assertEqual(pg.abs_url, None) + self.assertEqual(pg.canonical_url, None) + self.assertEqual(pg.edit_url, None) + self.assertEqual(pg.file, fl) + self.assertEqual(pg.content, None) + self.assertFalse(pg.is_homepage) + self.assertFalse(pg.is_index) + self.assertTrue(pg.is_page) + self.assertFalse(pg.is_section) + self.assertTrue(pg.is_top_level) + self.assertTrue(pg.markdown.startswith('Page content.\n')) + self.assertEqual(pg.meta, {}) + self.assertEqual(pg.next_page, None) + self.assertEqual(pg.parent, None) + self.assertEqual(pg.previous_page, None) + self.assertEqual(pg.title, 'pageTitle') + self.assertEqual(pg.toc, []) + + def test_page_title_from_homepage_filename(self): + cfg = load_config(docs_dir=self.DOCS_DIR) + fl = File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + pg = Page(None, fl, cfg) + pg.read_source(cfg) + self.assertEqual(pg.url, '') + self.assertEqual(pg.abs_url, None) + self.assertEqual(pg.canonical_url, None) + self.assertEqual(pg.edit_url, None) + self.assertEqual(pg.file, fl) + self.assertEqual(pg.content, None) + self.assertTrue(pg.is_homepage) + self.assertTrue(pg.is_index) + self.assertTrue(pg.is_page) + self.assertFalse(pg.is_section) + self.assertTrue(pg.is_top_level) + self.assertTrue(pg.markdown.startswith('## Test')) + self.assertEqual(pg.meta, {}) + self.assertEqual(pg.next_page, None) + self.assertEqual(pg.parent, None) + self.assertEqual(pg.previous_page, None) + self.assertEqual(pg.title, 'Home') + self.assertEqual(pg.toc, []) + + def test_page_eq(self): + cfg = load_config() + fl = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + pg = Page('Foo', fl, cfg) + self.assertTrue(pg == Page('Foo', fl, cfg)) + + def test_page_ne(self): + cfg = load_config() + f1 = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + f2 = File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + pg = Page('Foo', f1, cfg) + # Different Title + self.assertTrue(pg != Page('Bar', f1, cfg)) + # Different File + self.assertTrue(pg != Page('Foo', f2, cfg)) + + def test_BOM(self): + md_src = '# An UTF-8 encoded file with a BOM' + with TemporaryDirectory() as docs_dir: + # We don't use mkdocs.tests.base.tempdir decorator here due to uniqueness of this test. + cfg = load_config(docs_dir=docs_dir) + fl = File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + pg = Page(None, fl, cfg) + # Create an UTF-8 Encoded file with BOM (as Micorsoft editors do). See #1186 + with io.open(fl.abs_src_path, 'w', encoding='utf-8-sig') as f: + f.write(md_src) + # Now read the file. + pg.read_source(cfg) + # Ensure the BOM (`\ufeff`) is removed + self.assertNotIn('\ufeff', pg.markdown) + self.assertEqual(pg.markdown, md_src) + self.assertEqual(pg.meta, {}) + + def test_page_edit_url(self): + configs = [ + { + 'repo_url': 'http://github.com/mkdocs/mkdocs' + }, + { + 'repo_url': 'https://github.com/mkdocs/mkdocs/' + }, { + 'repo_url': 'http://example.com' + }, { + 'repo_url': 'http://example.com', + 'edit_uri': 'edit/master' + }, { + 'repo_url': 'http://example.com', + 'edit_uri': '/edit/master' + }, { + 'repo_url': 'http://example.com/foo/', + 'edit_uri': '/edit/master/' + }, { + 'repo_url': 'http://example.com/foo', + 'edit_uri': '/edit/master/' + }, { + 'repo_url': 'http://example.com/foo/', + 'edit_uri': '/edit/master' + }, { + 'repo_url': 'http://example.com/foo/', + 'edit_uri': 'edit/master/' + }, { + 'repo_url': 'http://example.com/foo', + 'edit_uri': 'edit/master/' + }, { + 'repo_url': 'http://example.com', + 'edit_uri': '?query=edit/master' + }, { + 'repo_url': 'http://example.com/', + 'edit_uri': '?query=edit/master/' + }, { + 'repo_url': 'http://example.com', + 'edit_uri': '#edit/master' + }, { + 'repo_url': 'http://example.com/', + 'edit_uri': '#edit/master/' + }, { + 'repo_url': 'http://example.com', + 'edit_uri': '' # Set to blank value + }, { + # Nothing defined + } + ] + + expected = [ + 'http://github.com/mkdocs/mkdocs/edit/master/docs/testing.md', + 'https://github.com/mkdocs/mkdocs/edit/master/docs/testing.md', + None, + 'http://example.com/edit/master/testing.md', + 'http://example.com/edit/master/testing.md', + 'http://example.com/edit/master/testing.md', + 'http://example.com/edit/master/testing.md', + 'http://example.com/edit/master/testing.md', + 'http://example.com/foo/edit/master/testing.md', + 'http://example.com/foo/edit/master/testing.md', + 'http://example.com?query=edit/master/testing.md', + 'http://example.com/?query=edit/master/testing.md', + 'http://example.com#edit/master/testing.md', + 'http://example.com/#edit/master/testing.md', + None, + None + ] + + for i, c in enumerate(configs): + cfg = load_config(**c) + fl = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + pg = Page('Foo', fl, cfg) + self.assertEqual(pg.url, 'testing/') + self.assertEqual(pg.edit_url, expected[i]) + + def test_nested_page_edit_url(self): + configs = [ + { + 'repo_url': 'http://github.com/mkdocs/mkdocs' + }, + { + 'repo_url': 'https://github.com/mkdocs/mkdocs/' + }, { + 'repo_url': 'http://example.com' + }, { + 'repo_url': 'http://example.com', + 'edit_uri': 'edit/master' + }, { + 'repo_url': 'http://example.com', + 'edit_uri': '/edit/master' + }, { + 'repo_url': 'http://example.com/foo/', + 'edit_uri': '/edit/master/' + }, { + 'repo_url': 'http://example.com/foo', + 'edit_uri': '/edit/master/' + }, { + 'repo_url': 'http://example.com/foo/', + 'edit_uri': '/edit/master' + }, { + 'repo_url': 'http://example.com/foo/', + 'edit_uri': 'edit/master/' + }, { + 'repo_url': 'http://example.com/foo', + 'edit_uri': 'edit/master/' + }, { + 'repo_url': 'http://example.com', + 'edit_uri': '?query=edit/master' + }, { + 'repo_url': 'http://example.com/', + 'edit_uri': '?query=edit/master/' + }, { + 'repo_url': 'http://example.com', + 'edit_uri': '#edit/master' + }, { + 'repo_url': 'http://example.com/', + 'edit_uri': '#edit/master/' + } + ] + + expected = [ + 'http://github.com/mkdocs/mkdocs/edit/master/docs/sub1/non-index.md', + 'https://github.com/mkdocs/mkdocs/edit/master/docs/sub1/non-index.md', + None, + 'http://example.com/edit/master/sub1/non-index.md', + 'http://example.com/edit/master/sub1/non-index.md', + 'http://example.com/edit/master/sub1/non-index.md', + 'http://example.com/edit/master/sub1/non-index.md', + 'http://example.com/edit/master/sub1/non-index.md', + 'http://example.com/foo/edit/master/sub1/non-index.md', + 'http://example.com/foo/edit/master/sub1/non-index.md', + 'http://example.com?query=edit/master/sub1/non-index.md', + 'http://example.com/?query=edit/master/sub1/non-index.md', + 'http://example.com#edit/master/sub1/non-index.md', + 'http://example.com/#edit/master/sub1/non-index.md' + ] + + for i, c in enumerate(configs): + c['docs_dir'] = self.DOCS_DIR + cfg = load_config(**c) + fl = File('sub1/non-index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + pg = Page('Foo', fl, cfg) + self.assertEqual(pg.url, 'sub1/non-index/') + self.assertEqual(pg.edit_url, expected[i]) + + @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") + def test_nested_page_edit_url_windows(self): + configs = [ + { + 'repo_url': 'http://github.com/mkdocs/mkdocs' + }, + { + 'repo_url': 'https://github.com/mkdocs/mkdocs/' + }, { + 'repo_url': 'http://example.com' + }, { + 'repo_url': 'http://example.com', + 'edit_uri': 'edit/master' + }, { + 'repo_url': 'http://example.com', + 'edit_uri': '/edit/master' + }, { + 'repo_url': 'http://example.com/foo/', + 'edit_uri': '/edit/master/' + }, { + 'repo_url': 'http://example.com/foo', + 'edit_uri': '/edit/master/' + }, { + 'repo_url': 'http://example.com/foo/', + 'edit_uri': '/edit/master' + }, { + 'repo_url': 'http://example.com/foo/', + 'edit_uri': 'edit/master/' + }, { + 'repo_url': 'http://example.com/foo', + 'edit_uri': 'edit/master/' + }, { + 'repo_url': 'http://example.com', + 'edit_uri': '?query=edit/master' + }, { + 'repo_url': 'http://example.com/', + 'edit_uri': '?query=edit/master/' + }, { + 'repo_url': 'http://example.com', + 'edit_uri': '#edit/master' + }, { + 'repo_url': 'http://example.com/', + 'edit_uri': '#edit/master/' + } + ] + + expected = [ + 'http://github.com/mkdocs/mkdocs/edit/master/docs/sub1/non-index.md', + 'https://github.com/mkdocs/mkdocs/edit/master/docs/sub1/non-index.md', + None, + 'http://example.com/edit/master/sub1/non-index.md', + 'http://example.com/edit/master/sub1/non-index.md', + 'http://example.com/edit/master/sub1/non-index.md', + 'http://example.com/edit/master/sub1/non-index.md', + 'http://example.com/edit/master/sub1/non-index.md', + 'http://example.com/foo/edit/master/sub1/non-index.md', + 'http://example.com/foo/edit/master/sub1/non-index.md', + 'http://example.com?query=edit/master/sub1/non-index.md', + 'http://example.com/?query=edit/master/sub1/non-index.md', + 'http://example.com#edit/master/sub1/non-index.md', + 'http://example.com/#edit/master/sub1/non-index.md' + ] + + for i, c in enumerate(configs): + c['docs_dir'] = self.DOCS_DIR + cfg = load_config(**c) + fl = File('sub1\\non-index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + pg = Page('Foo', fl, cfg) + self.assertEqual(pg.url, 'sub1/non-index/') + self.assertEqual(pg.edit_url, expected[i]) + + def test_page_render(self): + cfg = load_config() + fl = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + pg = Page('Foo', fl, cfg) + pg.read_source(cfg) + self.assertEqual(pg.content, None) + self.assertEqual(pg.toc, []) + pg.render(cfg, [fl]) + self.assertTrue(pg.content.startswith( + '

    Welcome to MkDocs

    \n' + )) + self.assertEqual(str(pg.toc).strip(), dedent(""" + Welcome to MkDocs - #welcome-to-mkdocs + Commands - #commands + Project layout - #project-layout + """)) + + def test_missing_page(self): + cfg = load_config() + fl = File('missing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + pg = Page('Foo', fl, cfg) + self.assertRaises(IOError, pg.read_source, cfg) + + +class SourceDateEpochTests(unittest.TestCase): + + def setUp(self): + self.default = os.environ.get('SOURCE_DATE_EPOCH', None) + os.environ['SOURCE_DATE_EPOCH'] = '0' + + def test_source_date_epoch(self): + cfg = load_config() + fl = File('testing.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']) + pg = Page('Foo', fl, cfg) + self.assertEqual(pg.update_date, '1970-01-01') + + def tearDown(self): + if self.default is not None: + os.environ['SOURCE_DATE_EPOCH'] = self.default + else: + del os.environ['SOURCE_DATE_EPOCH'] + + +class RelativePathExtensionTests(LogTestCase): + + DOCS_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../integration/subpages/docs') + + def get_rendered_result(self, files, strict=False): + cfg = load_config(docs_dir=self.DOCS_DIR, strict=strict) + fs = [] + for f in files: + fs.append(File(f.replace('/', os.sep), cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls'])) + pg = Page('Foo', fs[0], cfg) + pg.read_source(cfg) + pg.render(cfg, Files(fs)) + return pg.content + + @mock.patch('io.open', mock.mock_open(read_data='[link](non-index.md)')) + def test_relative_html_link(self): + self.assertEqual( + self.get_rendered_result(['index.md', 'non-index.md']), + '

    link

    ' # No trailing / + ) + + @mock.patch('io.open', mock.mock_open(read_data='[link](index.md)')) + def test_relative_html_link_index(self): + self.assertEqual( + self.get_rendered_result(['non-index.md', 'index.md']), + '

    link

    ' + ) + + @mock.patch('io.open', mock.mock_open(read_data='[link](sub2/index.md)')) + def test_relative_html_link_sub_index(self): + self.assertEqual( + self.get_rendered_result(['index.md', 'sub2/index.md']), + '

    link

    ' # No trailing / + ) + + @mock.patch('io.open', mock.mock_open(read_data='[link](sub2/non-index.md)')) + def test_relative_html_link_sub_page(self): + self.assertEqual( + self.get_rendered_result(['index.md', 'sub2/non-index.md']), + '

    link

    ' # No trailing / + ) + + @mock.patch('io.open', mock.mock_open(read_data='[link](../index.md)')) + def test_relative_html_link_parent_index(self): + self.assertEqual( + self.get_rendered_result(['sub2/non-index.md', 'index.md']), + '

    link

    ' + ) + + @mock.patch('io.open', mock.mock_open(read_data='[link](non-index.md#hash)')) + def test_relative_html_link_hash(self): + self.assertEqual( + self.get_rendered_result(['index.md', 'non-index.md']), + '

    link

    ' + ) + + @mock.patch('io.open', mock.mock_open(read_data='[link](sub2/index.md#hash)')) + def test_relative_html_link_sub_index_hash(self): + self.assertEqual( + self.get_rendered_result(['index.md', 'sub2/index.md']), + '

    link

    ' + ) + + @mock.patch('io.open', mock.mock_open(read_data='[link](sub2/non-index.md#hash)')) + def test_relative_html_link_sub_page_hash(self): + self.assertEqual( + self.get_rendered_result(['index.md', 'sub2/non-index.md']), + '

    link

    ' + ) + + @mock.patch('io.open', mock.mock_open(read_data='[link](#hash)')) + def test_relative_html_link_hash_only(self): + self.assertEqual( + self.get_rendered_result(['index.md']), + '

    link

    ' + ) + + @mock.patch('io.open', mock.mock_open(read_data='![image](image.png)')) + def test_relative_image_link_from_homepage(self): + self.assertEqual( + self.get_rendered_result(['index.md', 'image.png']), + '

    image

    ' # no opening ./ + ) + + @mock.patch('io.open', mock.mock_open(read_data='![image](../image.png)')) + def test_relative_image_link_from_subpage(self): + self.assertEqual( + self.get_rendered_result(['sub2/non-index.md', 'image.png']), + '

    image

    ' + ) + + @mock.patch('io.open', mock.mock_open(read_data='![image](image.png)')) + def test_relative_image_link_from_sibling(self): + self.assertEqual( + self.get_rendered_result(['non-index.md', 'image.png']), + '

    image

    ' + ) + + @mock.patch('io.open', mock.mock_open(read_data='*__not__ a link*.')) + def test_no_links(self): + self.assertEqual( + self.get_rendered_result(['index.md'], strict=True), + '

    not a link.

    ' + ) + + @mock.patch('io.open', mock.mock_open(read_data='[link](non-existant.md)')) + def test_bad_relative_html_link(self): + with self.assertLogs('mkdocs', level='WARNING') as cm: + self.assertEqual( + self.get_rendered_result(['index.md']), + '

    link

    ' + ) + self.assertEqual( + cm.output, + ["WARNING:mkdocs.structure.pages:Documentation file 'index.md' contains a link " + "to 'non-existant.md' which does not exist in the documentation directory."] + ) + + @mock.patch('io.open', mock.mock_open(read_data='[link](non-existant.md)')) + def test_bad_relative_html_link_strict(self): + self.assertRaises(MarkdownNotFound, self.get_rendered_result, ['index.md'], strict=True) + + @mock.patch('io.open', mock.mock_open(read_data='[external link](http://example.com/index.md)')) + def test_external_link(self): + self.assertEqual( + self.get_rendered_result(['index.md'], strict=True), + '

    external link

    ' + ) + + @mock.patch('io.open', mock.mock_open(read_data='')) + def test_email_link(self): + self.assertEqual( + self.get_rendered_result(['index.md'], strict=True), + # Markdown's default behavior is to obscure email addresses by entity-encoding them. + # The following is equivalent to: '

    mail@example.com

    ' + '

    mail@' + 'example.com

    ' + ) diff --git a/mkdocs/tests/toc_tests.py b/mkdocs/tests/structure/toc_tests.py similarity index 63% rename from mkdocs/tests/toc_tests.py rename to mkdocs/tests/structure/toc_tests.py index 16a1318901..96c684c56a 100644 --- a/mkdocs/tests/toc_tests.py +++ b/mkdocs/tests/structure/toc_tests.py @@ -3,12 +3,29 @@ from __future__ import unicode_literals import unittest - -from mkdocs.tests.base import dedent, markdown_to_toc +from mkdocs.structure.toc import get_toc +from mkdocs.tests.base import dedent, get_markdown_toc class TableOfContentsTests(unittest.TestCase): + def test_html_toc(self): + html = dedent(""" +
    + +
    + """) + expected = dedent(""" + Heading 1 - #foo + Heading 2 - #bar + """) + toc = get_toc(html) + self.assertEqual(str(toc).strip(), expected) + self.assertEqual(len(toc), 2) + def test_indented_toc(self): md = dedent(""" # Heading 1 @@ -20,8 +37,9 @@ def test_indented_toc(self): Heading 2 - #heading-2 Heading 3 - #heading-3 """) - toc = markdown_to_toc(md) + toc = get_toc(get_markdown_toc(md)) self.assertEqual(str(toc).strip(), expected) + self.assertEqual(len(toc), 1) def test_indented_toc_html(self): md = dedent(""" @@ -34,8 +52,9 @@ def test_indented_toc_html(self): Heading 2 - #heading-2 Heading 3 - #heading-3 """) - toc = markdown_to_toc(md) + toc = get_toc(get_markdown_toc(md)) self.assertEqual(str(toc).strip(), expected) + self.assertEqual(len(toc), 1) def test_flat_toc(self): md = dedent(""" @@ -48,8 +67,9 @@ def test_flat_toc(self): Heading 2 - #heading-2 Heading 3 - #heading-3 """) - toc = markdown_to_toc(md) + toc = get_toc(get_markdown_toc(md)) self.assertEqual(str(toc).strip(), expected) + self.assertEqual(len(toc), 3) def test_flat_h2_toc(self): md = dedent(""" @@ -62,8 +82,9 @@ def test_flat_h2_toc(self): Heading 2 - #heading-2 Heading 3 - #heading-3 """) - toc = markdown_to_toc(md) + toc = get_toc(get_markdown_toc(md)) self.assertEqual(str(toc).strip(), expected) + self.assertEqual(len(toc), 3) def test_mixed_toc(self): md = dedent(""" @@ -80,8 +101,9 @@ def test_mixed_toc(self): Heading 4 - #heading-4 Heading 5 - #heading-5 """) - toc = markdown_to_toc(md) + toc = get_toc(get_markdown_toc(md)) self.assertEqual(str(toc).strip(), expected) + self.assertEqual(len(toc), 2) def test_mixed_html(self): md = dedent(""" @@ -98,8 +120,9 @@ def test_mixed_html(self): Heading 4 - #heading-4 Heading 5 - #heading-5 """) - toc = markdown_to_toc(md) + toc = get_toc(get_markdown_toc(md)) self.assertEqual(str(toc).strip(), expected) + self.assertEqual(len(toc), 2) def test_nested_anchor(self): md = dedent(""" @@ -116,8 +139,9 @@ def test_nested_anchor(self): Heading 4 - #heading-4 Heading 5 - #heading-5 """) - toc = markdown_to_toc(md) + toc = get_toc(get_markdown_toc(md)) self.assertEqual(str(toc).strip(), expected) + self.assertEqual(len(toc), 2) def test_entityref(self): md = dedent(""" @@ -130,5 +154,27 @@ def test_entityref(self): Heading > 2 - #heading-2 Heading < 3 - #heading-3 """) - toc = markdown_to_toc(md) + toc = get_toc(get_markdown_toc(md)) + self.assertEqual(str(toc).strip(), expected) + self.assertEqual(len(toc), 1) + + def test_charref(self): + md = '# @Header' + expected = '@Header - #header' + toc = get_toc(get_markdown_toc(md)) + self.assertEqual(str(toc).strip(), expected) + self.assertEqual(len(toc), 1) + + def test_skip_no_href(self): + html = dedent(""" +
    + +
    + """) + expected = 'Header 2 - #foo' + toc = get_toc(html) self.assertEqual(str(toc).strip(), expected) + self.assertEqual(len(toc), 1) diff --git a/mkdocs/tests/utils/utils_tests.py b/mkdocs/tests/utils/utils_tests.py index 9dc33ab295..fca6c0a07d 100644 --- a/mkdocs/tests/utils/utils_tests.py +++ b/mkdocs/tests/utils/utils_tests.py @@ -10,7 +10,9 @@ import shutil import stat -from mkdocs import nav, utils, exceptions +from mkdocs import utils, exceptions +from mkdocs.structure.files import File +from mkdocs.structure.pages import Page from mkdocs.tests.base import dedent, load_config @@ -60,78 +62,116 @@ def test_is_html_file(self): self.assertEqual(is_html, expected_result) def test_create_media_urls(self): - pages = [ - {'Home': 'index.md'}, - {'About': 'about.md'}, - {'Sub': [ - {'Sub Home': 'index.md'}, - {'Sub About': 'about.md'}, - ]} - ] expected_results = { - 'https://media.cdn.org/jq.js': 'https://media.cdn.org/jq.js', - 'http://media.cdn.org/jquery.js': 'http://media.cdn.org/jquery.js', - '//media.cdn.org/jquery.js': '//media.cdn.org/jquery.js', - 'media.cdn.org/jquery.js': './media.cdn.org/jquery.js', - 'local/file/jquery.js': './local/file/jquery.js', - 'local\\windows\\file\\jquery.js': './local/windows/file/jquery.js', - 'image.png': './image.png', - 'style.css?v=20180308c': './style.css?v=20180308c' + 'https://media.cdn.org/jq.js': [ + 'https://media.cdn.org/jq.js', + 'https://media.cdn.org/jq.js', + 'https://media.cdn.org/jq.js' + ], + 'http://media.cdn.org/jquery.js': [ + 'http://media.cdn.org/jquery.js', + 'http://media.cdn.org/jquery.js', + 'http://media.cdn.org/jquery.js' + ], + '//media.cdn.org/jquery.js': [ + '//media.cdn.org/jquery.js', + '//media.cdn.org/jquery.js', + '//media.cdn.org/jquery.js' + ], + 'media.cdn.org/jquery.js': [ + 'media.cdn.org/jquery.js', + 'media.cdn.org/jquery.js', + '../media.cdn.org/jquery.js' + ], + 'local/file/jquery.js': [ + 'local/file/jquery.js', + 'local/file/jquery.js', + '../local/file/jquery.js' + ], + 'local\\windows\\file\\jquery.js': [ + 'local/windows/file/jquery.js', + 'local/windows/file/jquery.js', + '../local/windows/file/jquery.js' + ], + 'image.png': [ + 'image.png', + 'image.png', + '../image.png' + ], + 'style.css?v=20180308c': [ + 'style.css?v=20180308c', + 'style.css?v=20180308c', + '../style.css?v=20180308c' + ] } - site_navigation = nav.SiteNavigation(load_config(pages=pages)) - for path, expected_result in expected_results.items(): - urls = utils.create_media_urls(site_navigation, [path]) - self.assertEqual(urls[0], expected_result) - - def test_create_relative_media_url_sub_index(self): - ''' - test special case where there's a sub/index.md page - ''' + cfg = load_config(use_directory_urls=False) pages = [ - {'Home': 'index.md'}, - {'Sub': [ - {'Sub Home': '/subpage/index.md'}, - - ]} + Page('Home', File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), cfg), + Page('About', File('about.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), cfg), + Page('FooBar', File('foo/bar.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), cfg) ] - site_navigation = nav.SiteNavigation(load_config(pages=pages)) - site_navigation.url_context.set_current_url('/subpage/') - site_navigation.file_context.current_file = "subpage/index.md" - def assertPathGenerated(declared, expected): - url = utils.create_relative_media_url(site_navigation, declared) - self.assertEqual(url, expected) + for i, page in enumerate(pages): + urls = utils.create_media_urls(expected_results.keys(), page) + self.assertEqual([v[i] for v in expected_results.values()], urls) - assertPathGenerated("img.png", "./img.png") - assertPathGenerated("./img.png", "./img.png") - assertPathGenerated("/img.png", "../img.png") + def test_create_media_urls_use_directory_urls(self): - def test_create_relative_media_url_sub_index_windows(self): - ''' - test special case where there's a sub/index.md page and we are on Windows. - current_file paths uses backslash in Windows - ''' + expected_results = { + 'https://media.cdn.org/jq.js': [ + 'https://media.cdn.org/jq.js', + 'https://media.cdn.org/jq.js', + 'https://media.cdn.org/jq.js' + ], + 'http://media.cdn.org/jquery.js': [ + 'http://media.cdn.org/jquery.js', + 'http://media.cdn.org/jquery.js', + 'http://media.cdn.org/jquery.js' + ], + '//media.cdn.org/jquery.js': [ + '//media.cdn.org/jquery.js', + '//media.cdn.org/jquery.js', + '//media.cdn.org/jquery.js' + ], + 'media.cdn.org/jquery.js': [ + 'media.cdn.org/jquery.js', + '../media.cdn.org/jquery.js', + '../../media.cdn.org/jquery.js' + ], + 'local/file/jquery.js': [ + 'local/file/jquery.js', + '../local/file/jquery.js', + '../../local/file/jquery.js' + ], + 'local\\windows\\file\\jquery.js': [ + 'local/windows/file/jquery.js', + '../local/windows/file/jquery.js', + '../../local/windows/file/jquery.js' + ], + 'image.png': [ + 'image.png', + '../image.png', + '../../image.png' + ], + 'style.css?v=20180308c': [ + 'style.css?v=20180308c', + '../style.css?v=20180308c', + '../../style.css?v=20180308c' + ] + } + cfg = load_config(use_directory_urls=True) pages = [ - {'Home': 'index.md'}, - {'Sub': [ - {'Sub Home': '/level1/level2/index.md'}, - - ]} + Page('Home', File('index.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), cfg), + Page('About', File('about.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), cfg), + Page('FooBar', File('foo/bar.md', cfg['docs_dir'], cfg['site_dir'], cfg['use_directory_urls']), cfg) ] - site_navigation = nav.SiteNavigation(load_config(pages=pages)) - site_navigation.url_context.set_current_url('/level1/level2') - site_navigation.file_context.current_file = "level1\\level2\\index.md" - - def assertPathGenerated(declared, expected): - url = utils.create_relative_media_url(site_navigation, declared) - self.assertEqual(url, expected) - assertPathGenerated("img.png", "./img.png") - assertPathGenerated("./img.png", "./img.png") - assertPathGenerated("/img.png", "../img.png") + for i, page in enumerate(pages): + urls = utils.create_media_urls(expected_results.keys(), page) + self.assertEqual([v[i] for v in expected_results.values()], urls) def test_reduce_list(self): self.assertEqual( diff --git a/mkdocs/themes/mkdocs/nav-sub.html b/mkdocs/themes/mkdocs/nav-sub.html index e4c265b5e2..a01d801b1c 100644 --- a/mkdocs/themes/mkdocs/nav-sub.html +++ b/mkdocs/themes/mkdocs/nav-sub.html @@ -1,6 +1,6 @@ {%- if not nav_item.children %}
  • - {{ nav_item.title }} + {{ nav_item.title }}
  • {%- else %}

    5q!RY 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 diff --git a/mkdocs/themes/mkdocs/fonts/fontawesome-webfont.svg b/mkdocs/themes/mkdocs/fonts/fontawesome-webfont.svg index d05688e9e2..855c845e53 100644 --- a/mkdocs/themes/mkdocs/fonts/fontawesome-webfont.svg +++ b/mkdocs/themes/mkdocs/fonts/fontawesome-webfont.svg @@ -1,655 +1,2671 @@ - - + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mkdocs/themes/mkdocs/fonts/fontawesome-webfont.ttf b/mkdocs/themes/mkdocs/fonts/fontawesome-webfont.ttf index 26dea7951a73079223b50653c455c5adf46a4648..35acda2fa1196aad98c2adf4378a7611dd713aa3 100644 GIT binary patch delta 30646 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 F{t4uIb!h+q delta 7574 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 b=>>xXvun#12DZ<~3snq)iK91=53m0PtWqDQ diff --git a/mkdocs/themes/mkdocs/fonts/fontawesome-webfont.woff b/mkdocs/themes/mkdocs/fonts/fontawesome-webfont.woff index dc35ce3c2cf688c89b0bd0d4a82bc4be82b14c40..400014a4b06eee3d0c0d54402a47ab2601b2862b 100644 GIT binary patch literal 98024 zcmZTubC4&$(_Y)Q?OXfSHg9d)wr$(CZSQ{8wr%e%e)p|<|9eyQq|;BjCzE7qGMTiS zyqFjeFc1(BuRO}xo^G_%I z2O^L=ATW7lM&^H<^*^2eAN0eSJq3(x4DA1L)&F4euaO6sK5joV1E+r+DAqq4sQ>Wu z0|aVj?P25hA?l{GgpFa`oP%>HM?@(=7t5y$lA|Hyyb+&}%lcF7Py zVOq>>oZbI%cmJ;c1Ox&!PmnY&6cmq2?4Nt?RBbj#@*S#u% z($dm;AKJG3Yv)w@yrS19dscW!&dp@T$utcaiktwRu?l%Fgn7##v*Q%&IaI$|O!P}5 zE!tXI-Ss#N&%~+2xwep6)=D=@bER^nrNZX=A{Jq3H3E=sm}xcLG|pUA-88}8wRPyv zPnoSTxscjcm{McuVx_s+*=h#*Xv3UB1T}&E{uxPi!CD1QZy{>6F_-GvT;_v+@h3%S z3~p6JKLUMaO+O0%W$iTHs4{|UN^?L;ts#@G+64bnV>gujTO1A$SfkJKhUN{&{#iBu zbrz-NBAI4CWjjIN*&fwVu4RubbB`IvgcJ!WV;{$}bpWy2K1lw(2Xe|eWcN9U#V^J= z0v&sgD$Y5Kh^J4utKJ8w`)YkScnEwZDG=2~oYvdtqau)|6HAhwqW$r>MKydMdi-xf z|IPEi=Mls`ySoS4Uu8Lk>GP(?uENKw#l^+NO;vrl>caNS*3!n4J~PMG6%1?`Lo`8D zP!I`IikK!Gm+D~0Tx5dT2;-4lEPJvvNz@Roxn4bK2&F(-3ukKoTzvdLw9r!ZsOd)GFakMtPqh`I$P>j#E63N~^t! z8t)N`OP-Ey8cNVPKsgcS6B*&w9LA&4rPERq64J$9K^)cnN)EQxZgj#nJKXDP(AwtHNPvj4d!y|3WE|h>aXutjp#eR1Va1(D~!1cD@#G$XK@| z8ScdxW>*_WC0A}fCWQ_Gk+039h^tbyU`-AaRQXE3C@|xuc#bIvB-u`7jVA9qExYjR z=L}OyA;5`@PuJUM+d|rr+H3CQORerU?U9!{Bot;XUqe}i%R=!=DIcZf5IBHt${UX7 z$u&nXerDE=@3Wd|0@Hz$q*rpVDJ+Wsi!-OJ!$UKaeXQAz3oz@z3unQS7l<)x)linz zAH493JdOfC{BNrjX7CVfZBLDtgiqO>03bm9Y%opN;dZI*d!CgC7s1So zx$n!T6vhxG4g7BozT_i+(EXciSh1 z*WKx5dLayUw$Hadz3+<5D}%BZCKe`cE4yNK&2O zC_2B@YGbYTJ=@>6O14_I7;gA)sBiMPW}zMqr`$mljy|@#K)X4 zywlOE7bt(D_<9aY(j=81rYh}wpQBZ2>BFX$_0y{XD7Q1jV-(PFSPU`4DYgBSjuXGW zB&TypZ4-Ia;ZDv{*YiZ4BK%bLvA^d#3^`kw)^(lO=^V#PS}I{JY8vD2<6?gDUgByH zoos%w5n5SA70~&_wmZ}=sE_CH+$5D%I~M^tEkJ<ZQI7BsvH)rso$j0Tno$9{71< z@V}SCAhApjLIvlX0Pxk%zZqkf%M1LSF2n#NI}?5xPC=! zobSQlu20xcw~DY&-wOel-n@?qJ&by)A02bP=f7VUb$6h9A&zxij{$poi1x&>usk&q z)o~Zd^jeapPeoI1Jmh>Rc-6+ws~2@GiSZz{hBgw^soz#me0J4++L57M=6^+@00R~q za2yth-1NjYw%qz!q2gOQL3>x?qI6L_n5iR9jUE#0ppndAXQSaxXgAAg+?Y2ZVSq`= z9KUjbab4|QH-zBoMtL>BP)ja&OJ4O?2yYF#*>9aH4X@u0(otsJ5@}kXX@!4~Fy4Wh zDN>w`7i{CSlIi9?H2YDBB_h~K`_cJqA-9`a@G}pVc;w6b)PGdJz9MqO5mS;`wb~72i`W#}dhh!aglheCet+(79kLz+P{)7XRuyhb{YxtDFZ#1N?6e^# zh*vvtce7F3I~yiY){1)rPtn#OV%8zxe}b9$IU5=66PVl01yCBSd^dXUKhK1G0R|IV zcvk_Ac>q2IN6uR13{;c-_cRbEqYJTB_{Fr4IijaDP_s&jXx0$`sG}^H^o5 zz-Q`#Xift$p?Wb<=fxuzXVyNKg#>QnXBe)ocjuyk{hgW=c?V zRs~?RkX9n-Kuh2ogdASyGctZ-79U~PP*d!u<<~CRR3B7LYtxF8T{?!Nye0d%0n1-I zI4RC68nKpBKg^rfqiJ-i4HXbQx4>=dyxjLao>lA4TIu938pOX`7jX~@WPeN@jr_P# z^lTrnNnS5FJgePCzFZ$yZEE2?4_z#R){UKOsw3qqM;Tb8H@A2_3MP!1!fsit%Vn(B za_2OfhiiPV49y_-YDhUHAURUHq=tlP%rx5l^&mD@G^8z-Y=Z-tIt3L`u!>WVQxz;^ z&9LZUjm7~;VIecrymMSz9sAiMQWB|u=tF>$?NZ<_+~80;Rt&KJZ1cdqEdhb%EWus! zdJaxE0R*U{g1~6{#~l&e3R1mY+6nb{2=-5{7mcd@paR4GV(zxv{CelE`s$Ei#`XXd z)c6s?t)+nM8@GOItmYqze$tkR-@pNBhUdU3!dN9ILMYJOj4^aUvZMFQFK=P@cL1r6 z@U=sJ<=N(Bq`QQC3-wJHuee;+1OIT=^WJf^vichJbLK-(8A>DTum-ya`_|C7PvY^V z-X#zAoguBv{!+QTW6rx3-!1S_UiFDt_}ti$D*F?fI@AHKaETKn;7R7C5HXlh^h{!o zsrxdvVOX}7A?4Tr{6o+@q_3pMQZTg)Ea1)Q8|O#l$}N5<%GqV~ZE>N)M!~x7JUKA5 z9t(l39F)9Tiu!T`O`2ZQdW$v?+Qe4m558`xNHnv~bX8j4G6ay*PnvTLCWgm@K+IP1 z^SI~_P^NN)(Qy;gv`8wrCM0r zdu^7~mAS%W$G8dDhB^z`1T=lN-^sNz%Wcwkz4|)K)IQg@u1iEb91XhJ5xEwYDfvM6 zkLOfT>Goml>)dkK7RrcGd}4t$1w4`Vi@x?8r-Xz-T@erhoTTvYj;62sm##V72KMKy z7jCvo37#eEob8=(e^%k-w*#CwiWcoBL~yaY-mZ;3#7$hwrE0n&Z&_iqW9;qZ8h>;~ zOjAz(rmb4$^7bp}HHOIkg&1oXJz&O9f5ETRc`KDiwH!c>87$jXR}9R=#e{N-{typMNosUZX^8aPu^3Zb=_A_|$kJ2>CKI25a~u?@$|xUD0E z3rV0H2Dkhmtcz}Bqr1R;PGC&s1*q_(cw=w!eh^JIxmYy6ip|~R@0t~6h9kSKF8k`r z-rmZ)soKb2jgHIODnmo-1=6%KLu=Va>yJSJgYnC@P2eB{+<2U~g=4b-hjNb|x!65z z5!Z3c@32#?=kl#m5f8>l8a@f=Wi6&X>j+N1+ruaQG?CtDV~PXb>@WWf2Q($z>z7U+ zMBlz(Z=2s-T8$d;Ue6M3l3xRuVhSxm5s{3BKIpgmi-?-oisza zkmgcLp`Vnlx?L~qe?(H=WYV)H)PPR{pA7{5h`m_l^X{d`q$MOR49YduCf{c>9PI^G zU)!twAe$_^TtGrD{jAw%Wfw1k)5`DgJXWP`-7XNQ20MryLW6t0#t42k2 z0hnOio5PA`bpihQ)A=v&;|;YU&l?F@fC_Npa}OspB^Vr!zTb{NLwi)Hy`}19z@fr? zU3Jh7xd)*wL=El;v+()ck_u(iI_w^muPd_R6?OAcCyxtX2(vAWE-tjbs3u$PJ&jfGp*j;7`8P+@e0HF88@NU#6t?jH*EMz0L$My9PHiB zRVebeoyHC8Wl&pm$IT(G**{Utw9Bh)HAE_^TCH*ta-8|<-fxJ&aV4hWUSV75)+$)r zdIu%X^B9`Hh`wv*IW6Ho^#zL)v08Di99QNKyQ4Ex^x@3G;Cg6K(hX}D-{D_(j!D%6g}xd;qA)E>mv@<*$ZX$rUpcaK+~5kxF2pAac=%N>3B`6+-EO>fzLHkzfcD>r`}fy+!N&}- zUH9`HP&unio@pV+24r=ON7xE68a7?3>8!kAzHyK4Lb=YbvQ+HBn+||W{Eg?GVcYQ!l ztSPK!t!;Un>i4P0$ET?I9pdIh^EU0+RcYthPqRm& zPB}LVBWJC5;`qzHr{VN*QZ9;5?qvVIY@^viP)2>OQxb+mdkWDzLq#%PR5z67y??M+ zSjDiw%%q&n3QENt>Lwj~Ps8*c{0xvFm@csrU=eyiH}Cpb=6h0&O92O%dTc0WV%R`6~bS z;QT3eZTz7V7f#K|S{Kj{_}e_u;Joz^)V0uvH!H@e3WnVKG*Y;R5RQx=UKb=?4!qeb z=_DKa-vz<$?}ZxrbHii^hC> zLN`k`gS9^kaeye-(%)p=Q!i(kFa)B=q#!VbG7-calS3zKZMl8Kg`I^HD#h_iN?($! z>66rNVaPiYq<@#JX$rYXkw1$h7(yVDzNky$V^i%H!;0ZYI+ZXhW#@zfK7#lXMnh2Y z^3kcr0*7W=&Ss!urbd>4di6HWv0K><1f+uu%DQIF7AJcpusQzmE==J_e z-fwZbee~KU31mUe(k?U$jD<>ni>OKvN0|-t=m-(#j;6O&G~<{8=r6^gv3$D&K-xY8 z-A~Ae;#6^CAZ`&J{>W;EQAqsZ`r@~1+yiz(zXcIDK*GBO!0caA&f@eEcUcd0SLAp% ziK^4%9xfj7AK-j%&m}#)l$Krz(B|KAu~u{JsH3mYsRF-@7#pkE z;OJGjbEEV%#{Qt8>G*G(Vfh9<)rQPk1eaSAEZCJ)F~PoR(h+g}tl-VX($ zYO0R@KF7}dH^^v=pHnQ9YSNiTJWm+f!v@BwqQ$Y$ei`a_1{_|I-ss`3Ry;b`bNIE$Rnb+z+c*ky}aexvI*zKtJjccvTTZIqk!Rw!$+NgN&BT7q-IM^YM>9lAFF3qsj z{Ui)Y_-SRrj^=N_HhESJD-ltQtL~Y=Od(%jfPRpq8P9`F;O6pc)s_oF{z{=|n6er5 z!u-{h;{bvm_L%5agg+m)4aA0YAb@K`Qv~YLWx~sGmt6*V!|?F z%7PdL2(eqp+SqbvQ;>6xmHK-4tnG6El;(blqDJ+}Q2=*wlRYGBr%&K>9+K^{Aa z9GQ#O*$%Ki>UYmph71RnuwA?#!9vfTIuG|p%N;AWWwB5C+IE2*>xGPGkT?t@?Dvhd zt%Wpg_71*1_@0kBba@@FZN^TvjpVY+rkq1h2gtm zJPXCjvMjf7K+`s#pH$0kv}>*SPOV2H-e;NChSuuNAtqhRtEe-DVqBG7vr*enVEmVd zAv-&^RqMyAthD#nN)(w!Yp^GI_VB1e$~skiRlP3K6DJObNVTJM{r0E+{x$grTNFbh z_uBsc88W7$jtTI-pPGD>}Uj((F_m&nMmhI4lhx z;SZUOC;SP$w;q=0ux8Ozq190iFGeAoD%-HBSfOO9W&PK~Tem;KeV~3gA0dW>Pv6I1 zYNn)N-+Qq-I+AJB!=V9uxeoR-tL7t;-ZGy%%>9l;tMtQJm7z}(vh)}z8v;!QqkT%c z`Pr;kXU{<7gZGe(<&Zjp1|1&SGt0&iI1JiBIdPElDo}oD(oS=FPy1_j?dy9UkEB(@ z9bfbpt~myqXy`*o?NPpA2S*3Iq3$t0QzT^=d^GlO7pmjpsXe^IwU{J-P?mtkdD4jT zbfg}pfa66t&>R@5s6DBCTElqWD~=VAB5A$Y$g3nSX4Ol}s9ozugn47sFrns|d)D7D8mh1^h>F8%3W z2a5TI9W)%RgrtE1+L(i!DwwV@xZ@VytBSnvu3ay?9Y$%KBd@=bFp#4X>B};lBl^>;B5%>LW8TFDeNLsW?@@;#fCxMm!*pX9lfHt)uuajgiV$d zT#h**{Ipyhjltvp#_fvwZ6(9T&)Rb;VTsa~=gJDe$;q~EJzFO3Apn2EXrlA~F^1;i;H_jG>WmV*SvFHky zf3twjY=>%B`6@dr95pk37;>@x#zI%UP>yJ?6%2RCAY-s(SLIof9c#sG+>FEDjD6gU zD+r3UOyZKt5Q%XW6oZUQHH@|K!@vgu>y(j~#NpH5x9l+GPE6*P91EzHBE}krNo7~5 zb|0;8aj<>dJDCakJW=LK#vk^V^`8D9UP$2lLk&K$X+Ag;(w#ZeR7?dFGzJkJMi;Oc zoicM8#T@0|)<b|u?YyW0!6Ew$>Y~pX2XU`J zDYoQ`d*fm7~YwxoZtL1W7$X*5n>+fi8oUqvJri& z6nm&FFcO9AAX=7k9_;yussklMDtxu6t5OkjY3tvL7s1PUqGstoYssPT_ItLMXX))Z zJ03DK>_IPJgIKX7x8Rw<+?!kIc9MEA5hw)}5-iqzE8VFOr%mr5VC50inCtJ#tAQL} z1%tXg16rH5cZ?pPJcaYO6~hh*gGh%x5*s)RLDozXG<$(Q=kn_7fh78e%R|8C^X%4F zm9*vMr4{4*^7ibRo5iK-C*+ed7*^J_i&Im+>V~x=%ybD)(9wLptciZLN_)YB5O^v@ z{$Ja{Qtd!!GiH0^v6Ue$NG8nsD)~)N*JjWChU+1?Ny%198}eb+iG#cLFl;OopkF>K zIJg1zG{!THV!AKNdnO5aW zt-47+g@#B%3Z{it%Q@M`87PUsQr8-l>(V z7?crSbh@OEA$m#}=67-ZTp889W3?AU=1tjMdw;Ne(Izfm0-RQ+6jH&8gwGA_(Q}sf z2cqudmvKpmxhIPXLGEOm41F$3^s>mhI5{xLs3uHjw&8hlNfyhYWJ>LMMzm7Au8{{4 z-78CWHW(hd0`W;PqChl|g^3)t!&RZbm@=i00BhlV_)wg0=hMU42F)9g3L@3ao5I}H z8I}fZ8eb0a?<61oj=9=X+T!Eq!RN*aH=0Y9i8s}rg8IT>C(zNJ!Th>8L<=0PZ>~y% zhz0Bh?ag(U19g*K4YsztBIx+FBiiPs)+@S)uF6ph=|=6xgUL*jcixtPvskp*56`B0 z={4aNiYE!i0tq@Z1;pR-k?I3o>lQ~?sYinu)T9ag!9h~z6;ikT8&2oT|A@)-z( zaQOIKXY~=W6~KLycubCWOz(G95I!BBDB0Pny<_|zlgVmqx-mrqM_VmHhiBtJ`$Z5w zCPrd45%V_Ko8gYvDbKOB4l<(Fy#)}+&?NnmY-1A}rTwO$s?$(4W6U5%XfMI)w58zk zbnp#zcaX9eQujFlW$d|exgN>CX+D9ODCFX{GoRcYei!0W`_4DPA4@ELI0BSq?GTP9{qy5{Jp>{!$ilU=1r*;&BcRg z$*q-IA(UIbR;y$MuoVtrm}_sru-Iv6QF-Z$*v_HQLPEzhFGyrl8>MSf`fNpzygHW~ z_QJA574ufXwN23TR!mhNU*^BKQw@5<dJs*_=x{mDYt5qy%uW6HuIrYQdUw=BHHG z5Nt@%wEdaq4{)mv_E2B_!pNn?M`+Gf3%JA^GCHQY{6Z+#==o?VMBVKN&I-5tw2=+-ea|`(iVDzDkf` z_o4ZdXMG*j@}fOMk`);6@zP0?jJxg|pqYLnuYp;NEjq=E37d$523+{9c|=_m;Y=FC2zr0q z9ABp`#xa?^D8x?{^m9Pb8P5(LYi&GbahTA*2ISmx(8c(0gM7mGV0*-m^P2+5>2y*D zK>!ty(}TsN$-pvPyv8MaFTTJ&O7I6s@>;4;BIl36G56wWqHwlP{~pWLHf$Uy#0Puy zeV;G?gvis^Jxj`$>M5o?zm}_}UVzVP!9jt89Pwn(1x#nRAN`d2;9sJ`tk0AOz$1+E zH{8RxgaNe%M&|1hrS+*9C*P^Q=fDJ&p_?m6QWaQ!V5kK*vuF%HaecM^I*D{f1%Ubp+IA5m}APs2n1ZJu)J^J{Rl04s^nuyFN`DfFR|@!RJFA-DyQV<_xaV4SNKY62@hT@DgkLAq~ zhG+%xacHfgNfA`ZaU>zuj+4n`fU3TLj}&960XK1bcKm{wvmh9SVn*;5QgF*KxDXp> z;Zr51Q6HgH%jqJevB^Jiu6LMSlE`WNR1ubZUzzA5+#sU+UBVg8!D?yT@>=FvY+EEQ zC!*yn>I=^d@TLt~CRiEKJXWgp@5P+?!Jd%4yZjSDVZ z`OkMD7`^B2*g{%}qlKpgf7Zmo0$lvg7&BQ)Aza@3G~b|J$Ysk*P8I&CB}bAMZW-~Z zIR_wi6Up0t%hZXSOGa=}k*;=(xjt200^6TTRMf=`GX0xknXv$dY&rT#xsb_X8RNyA_$By$)d>6vNs2f?oR!rfdl)uT3^wm? zQwUBwSI&b&0r(I>$MjJH`fi%N1_>bz?&Ie_?js~TGj-`X%$+E9%n{r<<}`S$e`-p) z=*`trS)6S1Q%@D>CURjquWCtl()2l|<=i+Y;!j1i7jdhWpckp=OwWUJ0MIi}l3TJ6 z%ie2wuVKrrw_6uhff+-6)=_Nlw(qWRJwWbgGK?~1p|U<-iQ8R_>vJhnE;jiLPcBi1 zRW@hF{B?5XRh6|AR&h%$^yWc*ouol%@U#QTr4H?XOSYZzd|Vm2@o@5F7Ops_jl7Q) z_!ybL>GEq;&gio9wM`Qi-TlKa5EY2IY0@jteHNx%WR6`sJuJP1f$&aYFSPnLp{u4Y zEC0QDql)X^>kq8ecE4t_gb{C=2=3N2Gdry^aVqO$<8QdOeXI3e?r5`^^}Z(42qSR{ z0UzZY8>scj$7ip(7LQ+vQ=uIKkHj_~tcpcgSP5 zl5+MbW(cv;e_PPRsa@@MkrcgqMx5Z%N!L9-bn~Ur<+53s7!rjk3?KlB}I?)Qdv;%ICl2PJN$ftp)ow;+k%4wA>Ck$|vtQ zY_;32dscrw)Oop1ekSSV`gS{<%RUw@3VxU0lDzU1SQNO$YkfWP$ke$i6f&=S)<#|) zlsaMpADLw$TU8oa^N=>@h~Cf?=Nn=+j|^}w(vlxqQu54&1r>x{W^6ldqjSsVb<$rwy}rmwYQ01Baz>U?dDE) z6Enk8YWv#EPCC25t@EorUGU5O{POaAz%~D^imu19F!K|CcOQ6u9A(3jzt&6Lx23hJ z_sY^Wy`DrdJCS0duxEW>Bp16>_r;eS+N9O(hQNvjVv4ZBkPTG)KZS(quq)nebe34H)H7M%ti+!MZpA9N4oWcss21+ zAQwnD0vc>}2(d1Q#3z7x%6;?j6E#S26$>I+F1&^X5Yhyy)jZx2)-|Upucn@=gqJ|1 znjL{ulPOb0eXL1wk8Ah>PJa-YixeC}tZx!&A(kWBz|&k)2zfAfgt^NQ;Olk0Vk3P% zSYd$?<92$LGI`4r+F>*)w>2H8@J!QRnSiB-i2PD1f4t*yB0TW=VEPmk1ex?YExNMN zI9GtnDg}xUYG}IWCAHvEm4{~@{-51el6Asc*;aKov?K-kv&2q9S;tVToYnO+c-B=` znQKkgiC7CwY$Fiqj<-%#M!D%}%W?y{P=lzvRFF$pViFDB=NX-O>E6kM3WCB9`o^B* z{MM$j4lm`~NPO5-ia@%@awPiq@h@2GFf=ysU@*00s(yk}5oIaOg0TGff)nIUWYyxN zcEn}cZ}y^F)#s&R>KDsgsBwSUKb9_R?p87K-R`$x3itD)iTviK$x&+bcHFT*Q!eFg zNcceU!8YQz_sVsSd;ERa>;c4~o)C6(H5wX?RrI-;Mgfj(au5r*P)ju{uKG+ds!M@l zW?klvU;Oq*8pDCohHSQ24f7DeFk&%(PZcU>rFa>O6fcD4U}U3XS#+b?NZOc2maoDf zS5>B4E6*}7JnfMM)^Z2!u|FFCSETDqB*+}eo{nd-W7`sNQ!;2e+6~Ni)KbM22iZWB z%yRrZnm~6U0RBToY0kZLy)+s{VKacat74^qa)$4)&Ph1*?@Ov-g?MMEm?8Zb;eqt! zLvhaQgRdzKuk?`*jXV%Juuj*{CsQsj!V&}8J|X^iw$%6jIW)vwOI{HkFX{!z0lWlKgw@5_{( zOMVy%4F^Dsc0R@>XubIc?i6ec|UaBw?M>gea5yPFzj5S zT>m(ee^IdLw=-~?{o7xKpf^)qkrM(2p!((az6XGrED0(FM33D<0}i-zg79zA=DNXS zEsb+Zs~m#O<|j?o&r=|HRfL83{B0M~P{4zigdGU_Y0sk`&i#!eN@q9FI$Eh0D@$c= zHCwJI_FH!WbsFo5orbP4n^#UY>8;Ped9MS08=u=>R+PXtTkh6>nUbtX-mk~TlT<&} zv`4nQ78`LiHas=DuR9r3LjJaDID5~MGzV7ac6>D$N#lJ)K*b$#vtKZ<$~-Garg^@I zP>8fe%19Y_zr@ojHZ~{hg_(b+=~elZnQQ=ZFK<0h^nP0I2;dD#pcOcEKg%FDH|FA= zgCO~T$_6o8I$2SShA9w6s>(w(SXOn4pJ?h|oFzAC(qSCg$%!_$fG;Qnflw=yLUdWW zA)3k1AMBe)===HMKi6Z+RK3K-|6!Nf$WbMb-SFwgWqST%&t-)@hRVSed2jSKYbX^_BIu^IWwbNF9 zpJnu1Rn|Wqa>o_q$=jWj4UQukG7HKuhoijLbIp1FaSe$CRlFxs!%%g2>DL85wjvj( zy86kPCL7BS#|tDau=B}#QE|ffG7?kw$s+S;oe~>*PDr08^U!7HjxX!ohnTQt-D1S< zv>{kD2r9{5>ItH#v8$A+WSK86m8%+ql61HsP9hz+9q#mvT0C!ly1bL)-)G``ieJy& zd%tNl6e$!ua=U}>dM}XA>NTG{gA*PE_J3EIFWC8k4~p(C2wkZV>yfP7W~hmm#ntLo z8zO~R9Z9@lS@sMv$@L065Op;&QPR1FUw{cSF>(@B%9&rewXJ#8_cAc=o6*#1DT$xOzeycmC9E)Kw;29{@u_qV|P2(ZS zxS}xa+vYYvo$*1@$w1$QXeJ2ZsA|VX769oq82C&5=~|MRo4VlmF*%RSB7`4{P#pDd zHVO!rfZDXw4$Zpt!Il+oD?D$1+{uEk#nJjBK(eeJY%HhD`*}7)n_Btv{`Im!O4a(D z%EQ}+PvTbP=WADI;~|5XOqn2(kOqamX)kKHqw#y&_tnem731aRZGz5@?m$TdETNl9 zYS>UXk-v4THB7I;csa~%`a0{~6#Le+(mw=byX1PI&dDx!XDsGYB|_m zcnJe4os^9}S8d;{%WfLBg;;#j0-p7l;vBtSuFqcnEiu4ur+K*sVg3u1YtU+w(t}S* znYH047Q2SAnx}fb`rn$h^+M=ct#RG8&mx;^A;cRG6M`R-O{L-D%KMi~ug2yjTfo~> zH4VQ8Mvs>gE0<^aSeNJZh7>i+(1$u(`q{(nwWQK^YY{7>(QcDGjqqfWJw2Vyf}@0< z*0q@`%Zi=ABF2bB1I%U^tnxIB&zV$RNhKpCH@w6qHX=p|SL^r?GC$PTAhC+K`1sxu z=1&f_c)8l2Cc3u2W@J%(6;VRUbf0Btl2F`Y)VYf`m|vxeoTi>`gW96 zdvwr9$IR>Y)MUHq$%$rM=IkMf`b<@d5=nY#^q%C`fbwITF7v&Kd~K}4z;F$*^rQ0@ z4Sj#ac5hQzCLMN`*^3>aRyVd2a?)5z3k(T7strykphhh$nsZ>Qc7_&FaAzY51H=Kq zn4HbEn!l9dl5~X1xNQFng5l~P)~B!E-}j`fMweF^Ns421yno{$UANe9e-h$_dT3dQTzRcqepkzHk^z|s)HyzqDH#~EbY*nE z!3acTnuFHKm4Be2=5dmGaC(Z~Y(EH2Sh?kod(}((&UA6`XTR-YOn2Lq=K8Ed9J;;w zkQ210aTLZ=kK-~tSZUlpgbb=&zrtSoh^z`D-34aSz#KFN6OkBL#w9Qm3&c|6wm}xW zpST@|N0Y+_&$;v!^lp@ufMv?cYmi{r4I{lR1#NwKkwjJrH|5aRv8PE^P+iKQnnsxV zp9t{@(G&~gYy7pdSBcci0$eh7${KG?ZP|P5B!Hh!V~Ydjpyepjlz9e_y56W~f?UN1 zT}>?Ii^u;+sVa<|K{^5K$KG$V_fNK*c-!7`SKC-ilQU~8d^Yh?4bl^Be3ZK^lT{8= zS8p}8Foc24u}xec3~k@==9w{AJZg;u$Bsi94Ws6U%vuicdGkP86 zxPP_v64Oubdj3pnSIZt6EKDi*gaANFtS^9aDeN6?*l&Po^l(+nHNdVjB*mkA<#9R( zcBb{DRXMY=mRP1rN=ufcI?i2TqDX}okf?on<4}r zl;fjdikvb6STV!q@K~{=8VjL*l6Q)k40Kr!tD_9n-j}cIQH4J3L)rJNMja`rb^JJA zOox=e;F?5I3T&fsrC0_^(Yus3APsM;-FFE!Cx%+-tsa;5@zPj%AVh-)t$ zF+X@&4pt>X7%PsBv14&KggqdqHG1W^!jSt~HJUay?gXlvWsLkQPE0grR#Im*_Tl>X z$Zi}x0nE$Bk%)~}`lYFe!RX7JuD=ox%p`whlQ6|bqgsXfHaF81jT$YIL9{f(HSak? zpn0T?m@}WjLFh8hI=OyV6rERA*m#w}U1h2qzjXGbsml6#Jw&N*zdT-dd=15Ie+EtT z*#yE+H{;eR8(c31v!LGR%vg8(nR?iWQ!X zgB&?&SyDYVk5FD=GAgy6YMPzYc)U?f6w91AysneldB*ZfNwqr7o)r^k6yycj+5=oG zIsm{uOIXjQV$7>=Gfq1Zc(Qc~$x7f?D4xDB3DhOeHps*Sz*-D^I+uTCI|L@ z!^~0YFTBJ!r7pCmhdi8L0w%yf7id5|2Cex45Bt0=AS`Qc>_st%GM2eiFurXA8)&vn z(v1_c41I0zS)vsNNO%C$bu$RG48L{WZ2&C)?)C# z>17e@z3yu@{by7YpJ=5K$JiT#A#la2nF;S3f; zDSR=#+R(v$PoqqAEtF7EmCxP>bl;Bz4el=aO=r4jf0+oz{lpsf`JTJPo^$7U#Lirz z*rL0Ew*_?NZcc0iwo4?}+q1LDEVUGyv&xom@Y2<247cIV0>W%XhlS_CXn+GXfhKB1 zlkLEMF9fYoKw9yoIFBEbwmtAoO2?fPtK2%89$@3BqiiYqJ(gJ#O3CSZtS5)QCq#Td zD;_7RGd7geKFUW=+l}kCIyx@xSzhNHB=BU*rOC2NCU#BeGr7%XUc3KTRu(22MeP|OfeK}h6Sw$9 znybF@fKbPT$!GsTdDghElPCbj>FE=w$Ot1AM3OO`xCeU~O~LnREf(PRSZF*d#^Q?o z>;6J)+eJi7qg3szm{M%>vS1BMpTSV>egNC$?5H3hAr1~m4Pbo}?=89Nzi~9tHbPTP z;2V^AM16l1wX0b{vq4OIUpnQ|fwiRQ8kTb|JSWSTROq@C$lwruW0aX#qk-YnxK8H> zHw!#`jFjBf=_XQx5f~Oa{a_)-ei$&AuTgrk;Fu{BoqrAlS)sby2vM(P>jNt|rNgh>#=@{8vwQ;2CN+C+RNN7dj;t?ykeFtlMtesE?J!WjV9* z3rus4%J)WW(aIZ8p^48E4n3tHQ9k8b_cpaLHU+paT&KQ&zhG@L^d~+YM|w33YEs); zo?4rq3NcCzHtF8B$38y_U>LwR7r2++O5|Bv z#$sZ13Jk+K41jjkomNzn@>A+j*ifN0KeIZ^$OW<*yfL`NGz?~QZUTT{3buT*ARp{p{y4spA`#PCdq%(!t zgVbI=WSZrJZYhdd&(h!^D?ghV6EWy@F=6~$$K`8cR2A~~Yg!i~=>Q|o`GeD>@AK1s z*Uv*oP}N%In7?%8Abm7D=%i3{BPIHITKaU$uuS!$8KP0af*C~(-(~u;_{URw3*`*_ zdq{v!3xx93adJg%>3)ftaFArB(~d`3U&FxMhmx>t4)wF+v~l@12ZgHeOpelk^&}8 z>}dr$wl6ypRB);DsHO8~b^1t@aoA=_md7tRbz;K2)jSa&9J7=@>-9u+J;6&>r7Fe} z1Q+j@6rI;ze+5kFhp}4Uw>xg0GSfUi8Zhbz}Y@6}@->kHZ+jo_eNB zh(V%q_s&vwdO2BFfGpWxY$G-%v(_2hc5_AcDm2Jepu?qKUkzVEKPk4WM>j+2dM@ow z8vq`m^&8RJX*`fav$SU)?UJt_67BmEgZxsQOvV2JJV3+0J-Z{8?Apzzotf{|zIMm{ zv!jhM>cxsvuURNkE@|ysfs8o<_zT7QN@VBJQPZ3}3lcCuLXJ*(Vf-n-Y6LJ=XrD6d ztc1sN0qxRH0G(w}9yLBmu9JSRk?N^2Appkvq5mzs20=JsXT)mCPH|p0tTyVyWvdgg zFNy5FhuyPMb=0E4S|_06JTmFIA{Aep?DP~m+37hq-Z^Hn+1lxt zjM>@#ipY5E0K9@)7GY0>x+%?jWiTetLN0y zEVe7E>1ZOYDLtsHRm(ok5FV|sc~;NMl_AU6R$a+j>o`YW3Kwcu3mdMoaHyt8>hvJi ztWh>ls2=G!J$JBCIlEm~jLh;lFuvFj6jER{Lt;v4rIl!cMM*%Xx!m-4piw}Fxh>dAv%`Oh{%GoMl%m&=Avcrz zha=aWj=EV2(W6)pt)ZS4nWhCY?9WY&>4|QM(#Dh+q|(i4CW0erg?KVggqHH&GZrj>>FO8onE`P~>Jp5+Qe*(xghpone*3 zu1DM1jR5gVrXYiMOB;=6>H$|z)2x)cOke3Fn~-#fv72Fx=vyIaCjK5x7wtYu7UH2y zLT24kfdm$wx}YVs4BMkNA>nVV1`C;nts)i#B-$)Wy&Zc9@e*t@B2jO_27`#O6(d3f zQ70iH5)l(4vDyrxo=5_+I*Bd`ZwZPf{sW51Mjs9JdX%( zA>}GQiTJA7Gl{)M} zh#*o$5avbfvtlA(tb<&{U~yv6rqjDcLB!Z>auT6hXE50Xt6vJsSTIUh@ClI6sk78M z1cEWI$09;bEVuyMDLC~9Yl2At^On5i86XGx%Y{aA|c5HRqkDqve$iyKc zNpBn+=_%prn2e*^$A7B%LVg zWb8%&7H(uS14v;QdcBtj&=W}%3^t`B-iD(fdyIE)BbuN+J z1Hjl=s|20iY}O0NVkM%7POR0$TLmwSrGY9}IG_Rm2jl^`t3p2+aIGK&TbgU&-=>v>s+%nlBRP1Tm*_D-F+c#|3O2I|S|Agvju6c28f}K4-G;3MQTwF;jYKaR z&B!iPI|xqze2HK&#K2`YN;M;x*q2|8Z3>7gbgv0;-zr;{WR!>9^6WaP0KdH^d8 zVS^|P-yVJh>H%cIL|dzaX{L}ypaNJ{SQG$?t3+72Myw~i4LU;%adVx$%IfB&Y8}&# zaGi09w=$Z^MKvKyD89a^kxS)QYXQue!~|#K*taO0lHl@apQF%FEBv{_QmUi6UQzI| z=)?FePs_XaXv#qCyC&Fd>TkX!Jb07dYA@b}{2r1=Hc~BCd~D6bXn%C-9nWb@rC_bG z-gs|kjzX! z{0(PIY%gm5;t%KYP}*An+WRJfV{)o)schzsDjc(KMa6}i>~*TltlOR8WL2ggffBez z{#Ok(s$B3f!*-nPLw`W;*ECS2V!nLOO_Z@re6@? z_~N%!=oLKu5cbuSvwSa@ilceTLf3Y;3y*eQdwYlAQZRPiL&yIL~}Uiw~k zk*Ck;F=Z3DM!pQBXD3jJ@sy@YK~m`>Mw-nmD+EQg@t_%5tU%N!(B=0-r%N9Ux?g=l zed2yPK*f&%-H$GZ0NH0U#poRxOM@mT4EL^ow@$B$T*xrLR{r(-BNu zi3t!xUR+Fp7e0N}9g8;KEcWf_nA$7wxdS&2AG+~?jy~~bP52Q56fT^HE^BP^L~8CXSa#ff_m0%s zZC6}6HP)1Bg1^|*ORw0rR){m%Lba~=sqDg2^A_GDY`eQA;%RC`>se$;Pwjqjv+yAo ziw2^{|F1O6x^s;(QIsPOiO ziw`Wm=*Nq9+_ZH0awvJUw`k)s$839Z8eDMHKnpdgNI!_BUBgPXNXota)ag8Im-lYP zXu`=S5$c#Ru>MfPZO^0JQ*Xl_y5~1(zx5=V@WQ>_ht~J?)cyqMjq72}nVEilkXn6b zP?ymp`-_q`P4pNDqG-w$F1Vlb33>@xcyw&=D&a#f06BR3^}(H zmpa4Q6HG9d$!ONIZ^*FgXohW5A>rbrQ|4ltnc-&SL?TYQnaLn1i~6Xw6)1#RaYqv5 ziXxZ9jQN8*Lu(}(;|y&?r~O2z&6#a>OJUwMIv#N1HH-H=aM#imMrqBWJqH#~)0=nh zH0!4=KCoxe8cAqqx@hkMdls*eAf@ga{AG*XX3o_L#D98Kb9~{dE9OMCSM$Pnb9BxX ztF#xg3wCJlJjwJ9RBSVgs}Y{d)jsv+BYv13Jv}Hr}V^v*_?X!fW?1+PP83)pHRp zLBA|9>K>+eLYA~uT=sNALP0$W%JdK^exfs(E_=km(v47Ih<*_Q(N989y8_cXbL!7g zQ-M9di#kxZRP5S**amTB`oZKQK!7WL!IZ zmDlV1z-YA3)M{L-%V2h6l@rl*#YLhM*Bk)7r3FnQrOd zxmsB9{jh6qm1n_Ui5W^N*NwjuIh zDv_kvrYJ=-3Ht>H;g(Gc*Y{4IG`XhfYM*XWShh{Etw(b&O>|=Qkl51O+fq~29J&RV-l}mAJ*F{yQYFKdO6j$mz5UH5H9OeJR^BrqBbCImq)JXt=8jaZOE($K+EIK zc*=uC)4OH&$jE7TSg_$lm9cgWTO&GRuI^0ksb9KiYi(OC!kyVp*^H1yoEYj_e(}0x zZB4EAu-zqDf##O$o360nC9n7I09t=ybhcawZ^`QQRhApfQSlx1PdCr&2)6hg!LYxrefHz?*Bo5hG1V19m@G9A zGgi!!*My9s)hES_vU=xtHuX18X`dVjHn;TkZ(r~Pn)`B9_|)yCxp8oup)A8O_L~Ct zaZhO$BP#oDALAc8HviN9vGtApMkxJGdBrE{E8L@FRPNkypFCxyo07Xs7D1pQab=r^ z=-#qZ9dQ!Nc%c_eP*E6~SNVlex(`>Md8}xULT37sP1M2%5WXnP6tILut>#!upXKY!LZ!58LIB^o^PRM0)Iu4MVKth5Dp^$Ke0O2O) zD$tNZxp@h#+5)BA;e}FKXiZCb3oS?6mjbc1`OnO*4j&=B@BjNgh_$o3v%531vop^# z&-46#c%*0p;51w2hak8?{yi)cPo5NG;)|lla(H|4m6aKt6SG&l{pcpHlmZ}-lVPS&85{;Y5Mk9GhZqr%A{xj4Dn9cH)-#oi+0E$s3k{i#|D_Sb=hN>&lb+Gqn>Haxk@WWbpmY z%4P7Tl=$Iv`Fw}A!nVHoiN8$V^<-b~6T8nUpEbj1V{|NMseR-A8}GlouNha)9<6Da z?_BA$Je40~ymOKN;cz_&|7qSG7j`!E?7D2?+S|RXPN=Xrq}D};-?{se2mZdW*}r{Z zam|FybEnqGD_7r|4Mfh_w%kNs!`O*FTSQRd1Zo{|Txv5Gbb^s+Ac|xhTf`O_DWTFg za`NH#X!rQ}u~k=HwQ6Zg?>RU24-E9*_X=2i?z!io|A3e;!@?b|&^~8fEO5)?qix0UoTI_``5>_HnA!vfJrG-6}# z__6%cH*b``e16-u=Yjb~;Cby=+aKO_V&~2iyXIbbR(mmr^s2`V^r{nYojCCp-1w&a z>{B=+CNHoB>wK0 z);6*cMUUX2|$Yqei7s%w7PUQH4LMqk(gY+B9 zn2C}hcm}8#3?<14jMkZu2w4(+7D-DWCDmnc9+28d(Fx^RQUw(O0RxZ>5zK)U#vDii z;wvF34*ANp2`ULOLVz*LtgAvBV9h@FASRK2A1TA9oP-G`ugnUNpaZ}JDYNn{9Db82 zd`Nxn@YtFnii-G%Z)6bjL5`kV`(aNyDY56Kldwmj&d$zvOmeW_D0!Kl!KB2zmd`_i z`)7(#u;<((TU8v|y8dfXY`-LM;}*V2?)#xuM-dgOC+@x(5S zMw0vP?GDD_flZLuzJoCg9Y*m2Qw~XBK?$+qsx(o`LU~04=)1gO%J~rhBIi$O_z{@e zP`s>^o$ zAq*DGIv9}$6MS`1i71v7Rr86@oMqRy&Fo!H-uWYFJUfTP{gtcu7Iwu|7kd+u6@7)G z-e&QM=4#-x1xSb`SSCLSR)BT$;GEU#ez=;sR(@*sg0}fKz5Ems`#~qPmQ7jLcJxj9 z+94nPM^M|ja%JbVv(Fy-ApH^)*YB7V@kG+^f@{H-a=m#o>i z^L13l(o;6>Z|rZePn&NTXe|y-^>8@emsO9oG9(NI)f*T0$?v0`HQ`8=zRDd?d%xLIB+O2nqE@Nq-+*_#C+VvjV6VjP2Ityoof&i9| zl@;7PM%F!mD#xo-8-mf`Il&;nma%exo+UslhccOUA#{P>uGNy2G9$W`-i>amK{vNS z^ceK4(OFTc#>l$o6jhGu63$_GDE`Ely%k$Frsra-v%;Jds{%NRo%nlTF5!|9IWit` zz|1RlA4`V$9V7`0GSDlVuh($y+A4lc^K!Gb`_=r^H@@gq?@&^Iw zYK&$D&H-ItUIWOP=}@IdJ_7c*Dh0Po-pkHto^hbGdq(pXLCNt7*=$$xrR2ds6cv2{ zxF_*VuK7}aJTopRm|J!{|4~R#L$VKsq~~J_8huI39Aa`{To`^}I2soLiSCkn~*E4ZCWUitU^n_ih#+p}bL+c_al zbLHQG`1fDsfV*s#F>t$n48li`=GGu^>_#KCI=>d#I@E>mTlfwX1@PVY2}t~-7t629 z|GuNI=j?#Lup&Bh`Yk|r#~tZAF>b=~GoUN5jo%AZ;Tk5{`{>#^H`mwCvr5G}q4&{O zAN}k8zn=kWVep$Xqb%&Y-~<{Uz$uEp2#sMr#SW_&AmS3M7$;O`cr;4TK^*Y1UDT&P zG8Qp9i-mbX?qf8fQDlG3IL% zSqbyGKjsf#4@F83l21pHBaeBE7;Xc(30}eTvH4UKL7u8FRYD4TWQwfFj=9%W2bFyi zcv#v4F>+sNeSSD%DwWAS#$H`lDswG9n(C@c)#qfB6w+pAQHxc%DC6*sk#j7uT4j|H zt4&40@vkDydUo{!gz0#)12MAWfB3lwsfB=hMe~ zZ@#$~i!ik_XV$_FeaI;3s;Z_n>qkNRp}%n3!eg(E4r`$^8pCoS_$Dw zER-@?yNU*B#BQvCus+3>;v2PC;>*Txw+tsmA*=T^l5Fw1yPU-AjA^o(2~(&J6eyS9 zfmF`eQeVoTl+A?af+Swb2mQdC#fnXzi}KG;lXu>)EYoAtiqVATgPyEhNw{FlR4KKT z*d|F>xvDdv=2xQ{tO`?hBu4bzxD|W2WuY;!W=I0I$eYXjVR!Nmy9I4#t+{P;P1n}i!dTGl z4%QVpoK>|Ib#)cBRZd4y9X=K-tlipGv-!4FM>kKHu=yw%{}t?67l}b3%hWmBkisKL z+$GF;xRjw>pt=HQW<1$184U*c=UOdD5UR)?Oom8MCQtSgl;0i&MH2L&TA+VAln*m5 zCNM&z1brE>NV2q?g@nvt1QKqdD2V|s&sl&nwk%8#$bN@inWaQwfZTWhlTr3yGRhS? zn6Wlrbw0K>-wx=eDJ%L8kK21c>=8uJL+m{LgaNZ3RcnReZDNDo`+nSGd>d5!_+abd zzOL5d6Qj!*CXUMrK1J3KH=-g!oVJYkF{l;p(&ZKQJIdHE;F_TP27@5Vq>Vw3B!70A zLT38A8vnJ3>d9Gj*sQMx9Y#z@|hsip2 zD5hQ}q_}P9gN?l%_QuJZ`ZrB!DA)%k?{M>e)xX^R;-NiUAnAB&aomSDmXm12~beaIJq-laFD z_~Mf_A?5AiaABKrhDZ{%*|3Ev4GMhpz3+!yoX*l5z;5rp;^RPbyx51+fo6-2bA{f& z7awYvf?9`GoDLGLD{b=jBOiWvWS{l72MMHxrvyoHqI@1%y*nhLoe~ek{9p%vYu!f< zUTIs|ike2{`c&+ySep$hzENxr9v$gUk*q6}ilH9Kctpwl1l5u0AEJ_q3lyaGElr?< zOcH~}?ORHt^dOSA6wjxDq14iSEVU1{X)Z=AG9p6k`$vV*iSHQ*_PqkX6xlGL%JzQp zrb%UiPwDii!92B z#X^zeXqY&@54+m2sdN&37DHd*kAT*r4+Sdlusy^XuYY9vTf&(E(dbQk_Z?U4zDoRx zgk}Q;19vWAG_Z{{vhx-n=0pYR3~$K+}5} z|Nr{>GvyyyUyKND$#`3i!eYX_(pfPrhu2Nz(x>v$^l6TtF8zNaKRnIx;bq47skm+g z7>mkhe;>%!^k1VZo_8$$uQ3jemHI!GQ6B4H?&sw77<6<%5#aLNf$<9DcYHHXQNO3Y z`hWkG{BL?`)-NNkzZQTD-#{Qb+}o%HL~Nt+?IXUd2J?TVcYojBcM5C5XdJ|8r5BP@ zdF4r}_sjH6kU*m(=D|t)AM2xM=ut!0Gf6KVu)Tvx(y!>0QqZ2BtYejuuFQQtfLtLD zgpkmY$nuzD+iNpM2Fka-5(w9fI46!In^P>%&wH`W8EtD9STd{d-A;M0*;e zifKh!OcLpbNe!m@bJC(09R&Sj*XHx@6e2VD90V60TPips-~);XUQS0NmH;0JW2;~^ z9F1c`W;7mgprg?ysQCJVh=WDiI-dmchjRZwLjL_E-26TLi9~;@$Lmd|Qc173Cx!Qk zFf<7S69b?pc~AorUi3dw!vw7t^bdGbUX3&9)S&GE==W-|BADjV~aZN6xnv}ZW(i~Eq6gz>hgM;SCRB$G!zOnAY7mri*TINstE6`d|8QmNF3M?fNx zOs2d;1H(8|G4n}|E_H<8qXG{?@DE4f01-bvnac6j!VGh2zU?-p*sd@IM#hGP2Lu^= z0nq<3!Z&e5xxNpV>saNIQ%c!V%CnSGB}SG^A#+VAr5k<$Y#d%Nh~(@U^uL%0lH$f; zjdmm#F0Td5SO?)&U9HZgldE((@D@tc>U8oBupb;4^YAf}B1h1Vl4XayLpSzeQZ6GZ z*MDZpMdf^3a-6!%SO?);{BY&I`_U7~O~G5JTw@)EGnBHDz5QUnTH-3**oSesW>8l% z5oYeN_8QI)A&zyBiJYm{!w!Eos;Kz+;QTQUQ%bpxp>l1_Z?6#?6XIA0QMpcA-7yZs zW20X#%7F_u#$h}bq5cK8lJ|&9r3EADmQhDia}Vn`^k-u?78&1A-+*(o_x#?S;B;@B z+;avnG7);Na?k(43k2t$?w#O!R-$`u&6V?eHa=Z>n&wpP(2Cqxt>C5Rqx2}Ye5)s` zk=M0?Xxg4n85#2U!4zHy z?N?x%`sqz(bHCXPC z_aNf{KQ}za}--K*7MVC)=<*B%t6N9($#_rVs$xPB$sFlj;+&^LXkdHKHO%l9!~s-|}Z z&}{F%rI__`>Aqj~O~)DK|5BuN#gLx92H$Y{bow9o(&g!Ul#@zGg1kk!G9$-k`z)1@ zbis{8B~g7F^E%@&{#szAF{FYDVv7C2+4AB3S2jz;E1}WxV%lWj4Q7*tWdp4%H{WvG zN=#ZSQxeu8(FYHIeRmY}|4{xj?{{e}R+Bcsb;Q^7Z=WA4HsF|Dk`4c06j%A&A7rs) zDe~RbP>b+PAOL?As3R*|A8y| ze63fwBj?<^;rhF8*th=P4H5ShptpNoN5{P3KNnr_fK9KrJ#fLIOQ%-~Lgn;Jf#!{i zW^8H>XgO(I>*@)+-u&#yoJHH#&YBnS&Y8J(+rruX!@nyBehccjhrgQd9DNnGB&3R` z6FKuUCXF3Mpfmu> zxte_XGQMnW?lx$+9`W6dT{k;{@l)*m*y93!F8_nNX`Hp=)ml{-xSSeXS2_Mat6QX? z+MKDD2Hgf#6>9&tb<-2y{c>#O&-fwYF82MalnlAjMBju-mmK<^)kHB0f+zk*g;(V~ zv{7c6_V2es!i@0mDlt<5e>lJ?5D>mvIw1-vQAi4+67i5p!h~8GbtAw1cIwdkhf;6L zZ-a`r>EzoWHR>9iTt}*-dUz3>@?;WJfCm6(F*jw`MetaR{iyL=IhR^NZJ>5gmy(s& zd#J~V6(7|J4F{+m@w{|6FOBk`_lDA_7Qxf!IpguurP=(nC7X`oeTlG>jkF1vd(7xx z(mY^B|I|H(G7lkvk?t|4v**bMjJ=!L%9OgF+oIcU!WVptrq$`uZwYoLM$iPCNRBV_ ze$!u$IwX&=qi%q*QUA&PB%c|_pAIGQAAS&xe-)8Bp{~{0sWNH-mew-9LA-_Vgb-{1 zFv4u8S_d=HaoEw6$)ZQZiQ8)?Vhj!L$p`n(XhCY(`;B|nQZ~V=P6v&sMSb8_;J8$D{l$4 z#-&XL)+}0a>`$idEb75!R4p}`+Je7Bj<>}m@{7{pC>koYs5xw;QVtuc7dnaRYP0|U zY8E>2#4E2o_R!n!(x3e8Mytfu8*8O1S4E)0?r=$KpV%N-%W5t-_Tc_X-wlHg{jb^z zI#cE~&-8#tUeKKX+(x1~w*oR%)+oV>*88HWBtV^qr>w?O{6C7S2Uz~}$FhQw=2 zNG>7k2PFy{=ZN(KyLDvzDeN3;K|#kl&d58OO<*DoWxy)ze z`3)+^=&IGc)4@sdm5jsCYBVxnyOMxck6D5JW3NOp zzLQ^}i!F@9$m*3ux_9i#<$U9xrEC~e2iP+3G`K<-w~_$XVIm5}Pg2D0dLuH~&=Zg- zOAu@nal2?-Sl%j0oY7w%E#x#-jxK=ZHzwY>Yj_@T+wlj%i<2?BiYj|!NAOAV790sM zqw%KQyXy@WpmBkN_f45)92}8PK3VwlV~VT_PaWg-umhBiDn)guL~T!794sBy0*T@4)%W=^;2Th|FW3vyNlPiKv%AwNdq5{zS;}a3izc4AXOId&HeiPdcSWfV zCV5F1m%-Y^vN=SfNj*XE*8-nn0nD2De5x;nqUh#GsN<;j;dMOX^im1urjzLJ7?aGH zDu()pSuW_g|3>{qtNof7c2L&ep}(Fy>jvGEXW{r-t3|p0J#A|1LRVSXLUx_x66R^LnM!_p>J}HsA6^_PFKwOVDp*{H6?b%quFIumldITL5G-q+ zr5;qU?vo^z(}=Y9Ad+;KQoYnRYOl%=tgbxTtq#Q}miV}Y^5jJ}8>0}$;96)0)6zg*EG!EZ2psuQ zo9zo=anEsIUsx!AE(UC%dtUmcFXS&&I2|COWAY;^Vh)&TgV*HUCjC$4*5IaL4+Pp% z6zK_oY$AE#xC11A{{0#OCrkw5>^hKjV{d~$*O z6We-)G>Xc*<$c2*hR1^*^pOmab||9W-f5Tsj=lv&2GD6 zUV)`JC{@nAKHzSwE=v>@oMqPR)_IIT*V=niM%RY;d-h-+t$gGQg{C(%k=gJ!OOKr0 zlFAxz$dyQBsIXBYsc_LKKxA3i3y@R|W9d|gSxXE{O5iJ`R-zwImUm>tLnKWb5Uz5o89GOdB; zwb1H3c|QmM^8+6-A+14cDEsIE`78Oi@c!4`g<_(wy{)R%7pe*C-AjW-6LzesU*6PM z-t6mE<{=jQkkNZl-8#Qt-PqIDjsE_1`+Hhu=;3wiKIgnECaqdMjX87G-h16$2}aj! z;`;W+j&L`r7eKn##jJuiM+LDDyB#mXkRA~t^B7(^O@i(;B|pM_WzrW6B}0vAD%561 zX&R+zlqNWPOw>QUaEPiH=SN!xZI$)D_sLk=t6*di^lXeLYxDD%6ebj{%f%jJVjneb zpc?qY{-_0GWMDxT2QX&>mI*Bqri!uQ=EqnY3IPyO5EjoG*IC&SJkJa4djG|}RW0)Z z;{xZ*o_D?{=&1^JuQ;p?YK;IwSRAAeujmd|q2uSz?>-0Rn%9!}Yc*h5;0#n$+8b)R z%jYZsPtL}tE(+fqW|7#Ti#7y1Dm%x`TD)XVd3Q~Ny|NqsL}HZIjRC-J|FYIZVdtj1Ra>x;1CUFy?oR0eeqb&+2=e% z$~&q)yU&x+xIagyW8NZLd1w0iEzZ_yoa4bRW|Nh>@_e#OrLeVvlUDzJp`GK)pdB;>@7<$p`HuiC$DPtZWNvO@KGlI(6RZ6DEme z6}VQuV!a4^0I$V$D>>!m6uV?)u5Q4JrB@oW@DT(bq-tbSxcu>02{u0U6G0U?Z+dk0 z7Aq9wB(F8-6GnEv{9p3lX-?24EQSG{8SLumJ`UyqRLh$cqmmiEds=*T<@xB* zVHJ?xp;f`(^Pdl2LyuE#hi(fZ@@u3Z^yHDx$ECtWQ;PW-%7?Ew)AK<*mWg&zAn>&# zp3hvJR~so;NiebjfYJgZ3kyaTV2pQ=X?|^{Ax6G~%2D-FUc$(w<p&={&Y211-(yzcTTRn`)<;I4W|;^f2$aBJ}s1dJd5rt`Qknxu^-C+ z9(q4Lc?uX;1bzrU?iiff$UGAooQj6GSLCmN9<09puDifoFz#n+TbX%j92DwK-1#wM8;kZc8hOXTWOdlrk!v(g2;SK#-^cux!keFA4IM5Sc;|DiJ&Mc}6jWbN6Y^+S9;oR__{BE9E~mL0O5f<*Tuox#%@ zr7@25ogU>&ovbe_mhk0T9_E1gk&^W^o|L?To0L7|qZK6_;V~BcuGxCxX>ty!CxO z5RFNr6Q(Vo7)uyI2+byk4`} zVj6{$eA*oOvW%srAmjK=LgF-BiGv^}^XxTk(ofBo)YkiHV_?8ZBLf=sjg zd>Uh|;;ZU#ZhTc8z8+pXv@M7(>feO&Z3xl_g6JZ&vpcw9Si2~?|HzQ#F??AShgo`* zUoG)oRhAfrd#mR7_wxGouoZ?g_;uk0$|17mLn}ybIft%fKJO_U$gbDRwS*Q`$w}|c zr$9yHBq|YolD(KJ#D3Q0AO}{Cy}<)H`d|8_Sen8?S2m5t(62RvM5Ckq~2E?EaN1Epf{! zbW=IyvY5gAqdUm}}cfVfXIXhj^SM|VEr3QlwhK4oQV<1asbP(k8~-7Cvm)go_7q?N7BqPS)$?!|4HXXLz(F@M zMSJsH3`aR2f>bgIW~Kjhib5Ls2gFHH$qiSGn38jNZW!^ZQpM{~J{r^vBS(snt;Ad? zI^>izQIb;*(NYSNr8ld7o<{8RIsDDh%L2u6!tDmB;y@tn9p)4|V*DCWCS|x#2Z=M6 z$x@n5mRdvynk6PmAmP}4`Z9rg0)ap=NV(l|qFDaj_b(IiQ&#N1F$XwfnG*Q^0p(f0 z&$oq+=-hYZHKhf&ZTjyt8Hvdi^y|ZUj$FCrjxFn{oZky-NFdo8;7(Dv8@Eg0 zEEz8q#6KSW!){H1?qWTFTDGucdDpw5aH&y}FMC1(H3n4ODT;mz=?^Ovp7pGViM<%x zFz}OOyaLgS*IVgul?EH?vTIG4rCY6rN+pS*h3L0_bwm^{H%b$Cb$1l77SlT3Y|_Hb zdxOE*yF9_}x>&e!X7$8zRRxyk?~sg_3u42D_GXc@7-nlsf{}K_TNjqCxWG~toL*HO zt?!9X3cA3GTRw0-j9cSjZAE3oiJo=24njR#<<&nx)lnU4ov=uKXM52*Yt6{u0^sc`Q*f9H zXPt-RSpg=Lk;5~g;N`&Xz}A|*qVRy@?H}C_N(7z8_Di!?ejQ_dY}$91U7k!b3mW>GYNjjw8r7aOGob3_51*en?@!+BA%Wv)m- z4UwpU%8R6RUqA)&S7A!B-AxfWYB9nxQeP#KM&oKE)6HzT4rk@yl7~>IATf%-t89NG z|4gINiNBC^?@B@4IR0lE+s`aItw#RUyQI(k0r-_IstTAU3hRv0d{O8%N^qjtY!>B( zp@q&x7I3d*7A)!KBxA22&Xnir!IAbamYEF;_}{$+Dd>_vvI)%BaRj zd;4%yS0C7zeo1}^d`lKAdC7Qx#zdX5TSNCt^tzWWk`v%AdCz~JKhlv69k>ydeY+s$ z@egSz1Cn+M&}e%e>KRf%vRfT>F)8kI_#)u|K7f=U<$$6i(xk`G0a{^_rn9BZjfZsR zz4)YITRTr@7aVwOtB13XOa}mL3&`(#!ChAdCW9k0@1Bj0Z1lf?;3+#Ur*XLp1HF$IGVpgX!?{~3hfpur|&OJ_kB{+8(>)LPD>DVP3ahB`+kD)PR zJ}5`(GlLnv9!e&YX{1Wa@1PxY=vXr8MZGkAv(pKC(XXI`y+qblR+hmclhNRmZw9?i z<=0>|$q%R*uzp*AiemnX+A%^+C745YOnf3Rye$y*hiw6iAALq~Bn4R_p@0QDC^~B6 z(TFXEflxg(U022U2?%LzD~ET`)PQzcIp$jN#_ijTd}QXfi|5?hU3RNDReGs-W39%_ z>5N?)-%j{$ol|=2tew3rCp;BXnitj1(r6k(9W@iGYCO`Ef|BOi&hiO7+vJ~E(G)5X z>Ex4Lg@>=4a?a#xJ9BCf3{j`RQxR|ofZ~pO0T}ukel^4wH=Uinqols1z`#NI$AD%H zW|zMTeB+Dw96AmF`86~>Xaq-bm4b^wuqD)ZNo?eIuu9Be-jvKxb^+Wh2gkVTOWmfREs<6p@(we=^m8 zsqmQempb|9I-@}^r|?Q#iukf%x0jCe(_phfi%HWA;$JU-ars)#q!+ZdZ{CszrdR)~ zdb<4K!>_Q8W5G+u?iE`;K9?lTOBOM{mv=0Zyt}^4zUs=Gaev)+L zB-xQk=L9LTbBZE6=(lIATIWH(|MLtNc5A@? z5p^Ec8o74zW~;Jgtfl~4&fEZ`&$F+qeZC!g1P6(cpIGis-{*r?4DB5bh2x4G8V_Jz zLN)3Me*hT30Lcj0?E>?WuoD+G)wOnZ)J{&{d74Up?yB$JKB=|JDTYnvU})YNGqlaF z==;IJb9deAk<0G~kk^Qx#q1$aOy!qYT=4JK+-Jc#O>q2yHJh8xu%E495x; zL|>Z~lY&7WFE3Fcmpd4AyF&dTmrQKD!0QSz{c#grWwDsT+Q!6XC0&+@w=bNrE8q&1 z6gYcpI((u_tL62DR>@V>S?x1vfh38vpkaV*<`!bLLHC62Yyb!PUC>tH?P{rS06jp$ zzi9|=n$!i0-L7%~f-ZPTK@h?%iG@C~Ian61XtqkW;@Z+?k2BO&;pd!IVT-!vkH-B3 zi7|7lIE>ksH&TNS+HFJ|h7RlmL*R@t`7cyxjMXN=?a@SI4mI+}TTj;z>*HYaO!;q& zMxaH}3bZC)b!U}JvKH!jt=1*_I%;~I1tlR@VAqU=w@GAhvNl(Q%Yx0KZ((8!guw!Mi7N;|xyxM)yC!W4 zHlT*<@?sSF%vy$)*pbSq7StN6sf($rs5_}gsb3IY6YLp}SIHt6S}lkKM)ZG_MSrRh zFQP8rTUgac2xYu`^LYt6sS1AS zCH)ME_k1`&z%XqQOms>-wvf1_EZkur4vSijfLe}G3wSpbSRy%0p4dVj7_I7W{I0HWjX@fgjS7fsmt##Wj^E){pUy?{bo1~jqeueyZ z`Lio3Cg`kI-GuV}FtooMrPIctuN`xPS5<`MT1|LQ4?%<$pS%sTepn9;&mIjVl44-Bns< zds15@*u~P2yXlf9cPLcU&^00A0tTC&uD?AJxxFq;|731O6KgWDO%)4|Ju1Vj_1;^;2^ebV9-R=m3 zIcJ?U)VM)@Y5i*8UA)-i7HP0pW2hP*1IM(MSZ(>@#g*e@7A=^w1PyCdkGaF`9pS>F z@T93oQGx0H1q?V!@$QB~D(c=_`5ufXT>56Wz`7n~zsSmO+~EPtWX zRUdmVy?%T=?w)Im=t?FnTsJEii3DdILz}4Et)+kQ)}%>qO-?WTbX!w5XR~qLO`AT) zY2Iq(QJN9t&GJ8hY1)Bx^W<+QKRg><9qN9#8{cG(Y>c-Coe^+AzRm~jY`uP>(gI? zZoN)t|Dwz(9}^)c2>-)QuMy>GResD{fL@`=R0&p_Z9`{)^etA4sS=*&rLU>XjM2*2 zBxU(U@OlrnAlPWmfxWQefE)pKK=xu`fW&aeDC5f>Tk+GPhS%(VUaQrZpDC8;IB$8@ zBgt!!x^4A7E%F+zJOpmh{C?OXH4Q%S>kXFQ0{Mr6U@W0$8v^MtlzjoDV1xGo{7>^0 zqcLkJ9Zxa;MyXD+hA-7J#Q=leD{S^f08?|CfPnM_U#O%SDl-Y{*)1SM_~u)=NDTf8 zd?Xh>^8je*>;zuH=k$66P70$^0wD1vf*^RjP9GW}2IVW>klz?zQ&JL~;2fPp@Pa{b z^T{+=r)3$M=5%I;Yn1#SF;BXjouuz!v7CAnHK>;x?@TDeRxiKa%Zig=|OqxZ`@T006KsJsT{LMft~U z6__JC>l7)U2!vf_^WZilWz^0DjSle^NVcG0`i z7x%zRPTqCo$QZsCv#51BFP97$Z3gGI#2-R(5tfcW$k&Y#4@G?$AJ8|d$_bN~Mm^>tw{GPWReo8)X^!-VC*mrFr zI3FYZWg^+g*G#kup*m8&G;r%hk6d)oBk&Qj$?zB{U*OOK_?Y@H|2YuNUYG}5^05&u zh{S!vT(ziQ%jdz^aycqTm-j*)7#xX|a7ccA06vzU(GP0IicjulFJbRN`UH-yY{z{8 z*tsx{Gm4>iSB1%P(Mv>cQ$p{#ghjmpJ5D2MQ6ljWNQR`*{M81KxZ?qw#1Y(uAUe$8 zGng|YUczGE54u{jJsK`543%`oHwrJVY@1Fq*DqbN^CRojiW>O?`Lpt>gy>lsZ~o~0 zw&>CY8k4c2WWgIRtgD(bCt)q{a^fFhe89$;pK#4*E6ROC@~z(-GTDqQ548cCOG_8| z>q|VlkAq!c+-=Qf0Pkz-@>=H1v51By%Z4o#g%?g*lGJE!hCAH>t){w$*ZEzA0WDut zsL=$5MAw@3PV4w;+M==gqk*31&DtAo;QaOU)A!3xPhFv9PsqK=P&Ce6r>%Wy*F#fX zl^%~tUnK??R&`lh2@b6Ct~6w{Z$vsdVYdzuD&kn2gtL=SeF?V@9y77>fksuSE*1)- zkH!QDhaqm*80J%8IbLaN4~>p9SXU8835MNsO3Fcbc-}P4qJ4cdj8{&+_DO4dxZ<`4 zD?;ryW0l|Y;#GoYqfHGfmL$yNU>n~ zf;7#C3z)t>&Twn}YAKo4q1 z%tL_cz%gK`S^d}^h=-Lb8cAYN)Sn2#pwH&BSUso(=|{R9k1XyzwrQsCfvHpy zGye@{$d4Mm?c-;@@mZi1!1|>ZT+j%;@46N)+qkfj<>f^~>64zis0YA&JHNsp8%9%G z6^vSZQS8ux20k7Mg!oylV3aL%Q)@+2NnL>sfK$|Q4PXnRYdZFpFT8Elq|3qG`RzCT zDLZhKj&p!(egP)yDi-uED7a5v-mtB20tDlk>fyFf`cwj@QQa|Wk9};F9)4vu%6IFG zf=<4}sL@(gyg;P1ndPKT2a;wvarc>G+beh~VgMy#Iz;`I%89aqcFrrX!VE8ju3Zw># zA2Oi1lzLCaEQPnau&^HR(=e(^ z+gN5N8lS=u3NqZP3elazYG*fx=UtMlS+Zb4%k0^an{T{+^X8*d*Z2A>SFWA1V|iWO ztiXf=@`pv9wpc9KPEViq2%ymnGhz4c=e=H^AMLRJ{OHg@kH_zyP?BhmEZ=<5i_FfJ z>C@X{qMp0)oDJh>GtC&X{`>@sT#*haUSPB0t zeJ+fqcMN^L8{SBtH}o;Q1G{xAxU=jYGT#>>NpuF%fhejrM&>6*-LlForgUxv%8~?B zwqSLaEG~qJjSvS~V()tF$y$uv7;vCCPreNG!>F}`54;YC*A9+*?RKwYXt1ogX+d){ zGb>R!y?H_Nf#&kEW-zTP0e`$9IkYNy&J^BYG?W zDsO5+^C*_Pz9pO+Cdv;qNEHZz2Z0f{=dcESr;P*gENxUn`)gEYzp&14Z zSmQcXDhvO#Dl7$d^9B)U z#}&}PU+6A^Kx^T39HZwg09c(CD*$$_CJco~5-0Yp1rtRS-kd zg1Ml~67u`pb|Zuwr{|4y;jEb5R%WMxr^qNeW@#YcG&U~-IfjL>q>3$NtPg0-bg@TM zCRBwPBL`@!uIhrzDja$PM9<`Gv;#s5w3|vm`^@xRw4T#KT1V4*8r%c57LL`j9HfOZ zQLBGkXP`NTp#??*W2})jX|*g3fetc^M$iDW0OM9WI$?pu?bLIcYHKTZ3smjs-vCpgN>Y0;{? zaC}Flo-2Zs>Jxcg!!kMXdnsA<=A= zboFPIHnns{$LqshpN|%RU~-w=%o-p8&VY7JwBE?cbAZOevKl>VUmdN%FC5CZicV93 z+gzmc^X2UL^Q_jkySJ4>rgCRhxVcy~fYv#l61#1JUqgEUsI3F^!~)60GYQsHYSYr1 zJtm|;@(mLKXec&S6hm6C1x1qG1IkJmlVETF!NqDECOv=_V9;8$0*6XMbH$9rAPJOV zOb!4HX33;ww2);Pj^=^T>@w(Ei?uXg&^ErKh-$YhZMu-{0x8vb51u#yJgky{SX6Xt@Fn=M`wKqHaRi z^3%F$ey!7NFT!-*YhxYOYwI?>c-F3R8z^#@9qCxHWApl^Hy74SDTUAwM?7x5NsW)kvY0@5ksMt`)l#k00_;^34AB8>^v4`y zbSTXD@GR|6=z!5!f(8mN8{+XG2mE}D#q&GbVWdzPUqwcfR#59<9I;^$1Z68BG{8MZf>nuNIEmc*D>?(4-D$J@ZZ1 ztV_2}+Bv1!^bvgsXszwjcTXz7s}LnKCU-PP%RRcCBlNHmd?ja_vGAH1`or-0n$~5! zaM6d07vHwLLofpNH}Bjx;h#5s(Omq+$J75pp9{cs_ewu{+chcHY?J+eeH0i95)GY& z(K6PFx)+VK0~WqC79OM8ey!AUtbbI|)c|uRM`}H^;(LXeh#`)LEe3>J9>>kn89PcV zREW1Y!ZfR(&ta)3h6x!(j6KKP7;aoNqo&tWSSFedmUonvRJf`eHa*nSk=)oGnzo?% z&{=kG_k_sonzGuW+Q@%D*!hEv6TyZLkL>N8(Rr;r_}oTwx4HvZyaV2=og1rg>YY4q zHoGh{oIbxZQ5j!cRou3*vt>zhP$;nr*3xjqTUqICu3UO)aPszpM?UN}Z+s50*LKe6 z-K*@#gLsGN=M_kIc!k8Wv{4--;wobgi4%PCT0&DC%CmCD;+zhK4gR?~c$EF#r49D5swLbYDMy*C(Ztpb2 zyXMdrtVr1JWLjr1Gk@Xm`>lhIp$GK1Ohu->EjDy*Sy9mad8fQv{*}dUtFT*jTG?H| zYwca^-uQ~XzM)SopaEP;jaYY3G?h`FnrFZ`#dc{TGlK!uVw>IT54lbflMIV~Qw*{9 z4pD@d91=?|vFFl4E>kEISBCws1_=M7VucFR0h?qeeoVv2S?c0aG(f9tZ6x*^$?}<) zAC{^wjTHU4@@s9#m6}-9Uo|o13TeNt{Bu#HwB8J;&UGNUt`ksZx#!aVxb)Kh00X7< z(mnWsOO>)RxU50qiK_~` zfzxc2Hp}9(QT5&RiHS=ml0TH*)D4r}o8$pf8ag2>Jb67sn@CCCl*i*OeNZMCf1tm6 z(2Ah)QMOA2w@u<5NcaN5DhCh z&Mh1yG1e?`3l4^`3n!K{<3Zvh%*F}XJi+i`i6gGV&Zd^!_Rgp8+_ps7fQ^hA2(a7=X5$VsO@1*7Q;8+7|rM`s8!Ay49Z#gb#&Hj{N@{js{8$vy_gbF52b>5 zT*Jc}M@GO%ZAp-0)S*s{l@Li8LwsPzVIqk$pU3K-lwW?l_t&S^9{p_ZK{Q{6mdlq7 z+>R+`x4r{|Ty1?8(%9&GL`m-TT?mwYz@#%D;BL4hnC- z1vp;a&B1Zwif6vD^@fv&B4V*ns$iRODb=Q3u6i&MbG~nsAOEP>mP8(!23(u}1*0=3 z$r%pwVEs^m|D%Qo(g(4^f*Ox0%oRI1yNqT`bkMp`PIGj5i zHVSXp%wp8~=PmuXVj<;1x~Aa&WZ&!P|f)F}$^yO}A}WyEI?uczUqORQNyr0TI; z2+fT&8ucAkLV?J(mJPP0zAWrfvr;xZ(ims z&;`!vy}FsB8B-Y$4R)3_Ypiu9b5X3kw9p7SQLAI2z;gx7M$v4K{>PlC)h+N43G|#r z(1`xB)?jlrgG6%3S#`i0uI1=&5+8e`k+KGN84_vXrDw6Gkf(rQtpS9(o9;I1~?Sx!Q-CPV9OwHpeHnitg+vOrVP*xOk;(P;2%p*dJXR7!dM_Fkacr%KcCk9>!A@(~D33l{qFO=^ zPys_@NV`;2${;yL4xtlRWydNyya$_pXWHyy$Lwtytx+iAEgr%1MCG40ZkSzNeWGvU z3Zx_U%cli>FPfWH`aZaaaDPs7^`V7@;|;}yyZ$-kpKKCb zKK~@I`!=JSW%b5lfz>Zx+f(9yX2r6l?xH7}dv2I4I6gb1Y_93J_R`+g_8m{1vlTGO z2Y)avah+g5y#O|~v~4vCdeosB*TWUdch#e(qcXJh7}3+6<5=UYp7d6?ORROzdAws% zROE{5t2x*7eA!|PrKKdy7f<+Yk*4jzYo3tDq|7D2%%g$QVrN9=+@mi%fAqjF{efS~ zx20cw;(k!VM4xyy{TL{@-@knM!fy^9{Dy6j-9z%(tKJ39XThZ3q|4;LzPkz>83KRt z{6>COS?fcx!%ifpZNO_UG!|7kiYF)^Xe<^WHXi`=am8?&#c8$}#G+L!()$?!X*g(j z!fPV}{*XDGWOsTOE$>~md{(pBvROXzrsQ%-$3XeolBvrVtz0nIx8RUA%ot z$BH=%5|!NKi&rjaiTLa+W6-##)Yl22NawlDB`jwZH9S&}gzDI$6_<3taLdg3^SYWW z7Dp}ToZh`-+cn@P-P>BcwBRYw={}Ob1+Gv5c;~nvYK#@r_ROue24;3uT-pz4NLz~P zr)`~FXpzP>wYAll%sV?d>!fL$HecOQ(Aj;~qPde}CKI#N#XH)fjm6M0^Wr%z9ua*$ z^z~Qpj;5**tU+Rn4aqKlV=3ZEZYA+mM8X1!&pxpEEch>I%P=xAf7?2{K^{tfF?%cX zo58Zo-`3gm%-LIkd*b{Z^1py_$NY(4@+s;Rn2LU`YHy#nV@IBxi4n?b)cBw=X-w^> z3GQN&Dv@c1WK$tBeek;iz2G%t@R=U{u7Iy$GO=3L;cTq=WUS(8%ZfQmaRGBwteDBP z|2qpipcWCdVP;f?kySqRouwTmzbk8|xnho#-$z*+sF2HQQNqqFRvbh79RX@7>|13} z!^RAup%=eLJQ$C@{o-64zIYnO0M(vb_FcRIYIHsDekXl^>f^o)$>cUFh9g0VIEJOM zxC76vR0Ip94l)|i3XoWwkc(nVgXFXMaI}|1pIX}}zxnL#^4GVW_>pDjA;3Sg=bi1) z-FS*JnoBKT$feF8-2*kkg4o36y&XYtzr5ZIepPDu2rPT`u|M1fw6{M2%33dt{qeGA zH|Cme$)G41-hGa{u1nugYic%i^xW~M_fHOcpL>7H zY2<%NJq_P+5Z|Rao!031B(oI-bP((?xg7Eib#ojr7YFw-a<9LP%<6pO8eTynea1~H! zjj@kC>McGZ!4Owez{k<#=D?A@K92Vz@e~N49MF+kIv`<)Uf^LOtS=N_hot2e47n?6B961WqG6M}P#$nCuIyP>bjKY< z%X+F7xqz1us%tw-z)M5gZJ3D#B4VQL{7}iJ63_S> z#>>A6m5p~gu~#T~6AXYiv4<#Q^cC2;6YBSYu|(z&|785JVhvHTA|a(Rm&_0}v;jJo z46AOeNW;t}Rd_qp5K=q_f;7v1(K>h8L-qW;rs^4{xcqWlGq1V2%M`z*$ksADUUB>S z+g$}(Kz=?aJ+U^!~?f*yHcfdzgW&gi>-+S|>w>Q0J`lKf_nVIxXfRKa`dT60{2_PL| zXkr5urKl)T5gT?aD7snuT2L3a;Ln1)xVyHs7a()_-}~N72+00)KmY$fFz?;^%6+$- zbI&>769Z*&=?HR_*glK7a&$buXKoKElE}L~AsJqgKU5P(FP2Kt>A9d{{)Kxr*@7n3 z1v(-?mv&@d2GXwVL+Kuy>A-2c3`wM#O$4gJKqV6TgxlkNDK@RXep=ykg~}XxX_&4J zmnO3Ndc&nvfx^c_v_tLSEk=XU!s8GP6uz4CbxqEk0Ec`A(>nj4L0PM^q(LcaA10Id1)q5Mpm{izktGVY2Q2Q*gQ*eJRBACr@puIbLIEL@7DPWm zjku>lcqhI;$s6>={lta0XyS>feU>+wg*6a=TgdV8SP7NI;H4T8kewi2ZsJsyKaS%; z;sXT7P3s%Lq8I`ZsuTP?D{`?0p>G*Nj%v{AB_o@h2R&;uI_84kDJ2!8iU{(6(UE2|vUSj0y=3{EPz<3MEAZkh4?@ z-}u~5geN5)?UET^(Mg$TyH4l@-XwIC1kaixiL}410I|9?8aO_!p4Hbli-VRA!v8_#;~WRI1yY20!=v6?X8MN?3Zmg^1^!cmM}mWf2H#pUM_M2ST>zjS z{Qe8iCfOTAofg0o0R{?YAoqc#xc_go)X4~&` z0@ru0ER4rW%N@18Hu(Ae>YSeNB8%V0-zi?j;{K{A69Jq2>txg#-bq;I|8C!nK(}n zyH_vOCP*VpL^&`hDAAMswTM3r*c@Tg6sIXcfNg>y-b_4v3)rTZo}wjO+R(#{4@@-T zkCk9<&_7_7z_Wvi8LZV-qkmUxwGzFgXw}MMi5?v*X^zF3!S7}-%aE$MaE}!Oy$jsTzR>bSvL0Td++;NVs(S)dH55%@kQ}9 zC6b&R$u4(6flxDj9-LF@ZezX+W#!?k=jO0_^u44tt1`zGQCZEaA9!H3)uJi}Coj&I zxbW;l5SbHc@Ueci6yXI$l@ljmV`)W|D!_$|qywF&CONJ1(w<8lLHq8d9V3?74ZIy( zxr>}SD=)ocDHw4f|8m$~J-mC-aP*16Za1u4-LYhGJHU&ngO7i-dY!@U;Mdq3YucAA z0S{cr)sQ*rPA~X_C50G888F~QV%`c z_X4;U3_0`YBYm4*z$tX;a-trS+WXMYXC4J|bUL@9A{Q>W|J&~mUQvEK`ti{-ryd5% zs&e#gPDMq|Kz@bbeNX}7W?XcSdJ+1V?M>C9tVx?-FE}x2Q|-X-+XGI(-c6HGR;qRr z<2+wsPl|swDaHH)_h=cuk4~_54+yw9WO?vdflmkUNCHFa?10A9=U@nWiX_|&4LD~oIt&J{VgAvV4G-hI#pqgGW-vSqTyMOA{?^xV zXUBdqu|GIqe8~iC)FR?rh!WUtV)HQ|q)h{PbGihv?SMkuCq{n3h?`nsxpqfR4E>M} zz;zE_X5h_o2?ek;|GJo<5eSx{NlTr$pJ9?9>3G4va`nAm>yuP(DYul~0kR zHfJB@;anW`_dSJ!;OFz(S59T0m2q$4`E(<7gnErSO1)40o%$#BDfK1w72!c$G*Qr3 zL#}}J5lvDT=LRMm4T=UNC5dW?rw78K3Ys^JNNkfO5zqSqM{Ukf*ie#2=^%oV5Sc&( z8#!}AO`8)1T&Mu%5Z5c1EOo&eU^HXmPFf@CED?oO%%#!fg7}F9$}VB%fCx+-s)kWK zG)X2O#i=o)2Gl_2&$M4#E4vOtwpB>|Bxz-yq#st5{-?!Q>L@(G*198G`hylksi z?Nj7RIhZ}X?~uAQPefLxcyR$w0~ljS=AUV)}eG5SO1d|eseqLIbM-1TxU zEtAXmIH%|vWy^KP3rg911?^WpQiR^t08XQjav&F~IC!Z+2b8I`BbAb30E8=xJgy#( zv42x$Op{HbHsNJ0nBEN``ms8qxjEnENpAGphYlatomjdb!WL&kQ`xTNtFvrvb%PDQ z!Yqd~w)SoGIeHuY<4?&@MaQs?LSEhMt8)4Cq#Mfe4(1yDqZ>vhLJ?kV@)lzb!ywOc z&@|(*bIQ$yYK>f(XE8`Q15`0`MnXf4TBDONN>FIZ&v%R*1;XX!VE}HK*mRAlM^*GZN`LxS7LC}Tp=s~i2@Nv2#zU{1ib`}XIQdz67W%>n10p53?ab~WbNn>tsHZds}vbw53O<>=-m>M_qWDs~HH zTzh)(KWA;Bv1KNl)nY4XP~wc{IYP$mdz=kVjZrLZ8@&>|)w9P{TVQPJTs3+~w|2~f zb;>=8z?@)!6oh(m$L6`@j`*Le;qX`uey~;3nhk|#c8*>(d9Wj|Q7AGeeM4961EUp7 z8FTBUiqTItq@OpP)sSx+HfxpWw?o9t7(|VuCQwtT+0;DhO6pFspA#$;T-Aj{WzJAq zLopE~)1ky5Dstj~g3&S2y~JaI$b|$QPf=x)78Epnq*OwXh9x4bIRpYa7MSS}o_5WE z)!|P_ZXqDTi2EW!U1GY82N%!@qU=yfNGE8wBy?;f4`&*6a62#?40*X+Bh%0@!os*| zNsDoVTGt4rv!o#xgn+e~EqXZvBmqTv;S4CRSIDdk18J*+wwBZ?FJl?iTQsK(x?DE1 zngO)OP~_)z@VT0+&-@IZNHsIZXFWdSue0)xp#oTiPTv*}Z`@Jt88!Ty8mU~$I6TbI z2L?~MZnVZ7kb|9lr`4$fPQ?<1Xbon63m|56D;NWKjpn2>gOiQH*=@$F~Vxs zSpv|}e>?!{|1Q6)CtR9JGRevH=e#T5>0Lf3Ma|naxn4qrOT+jvy259Y{ndc_VnKA# z)c>Xc*bb=Da1Wx0H*catFQL-1n;L33o&y$9>je*j4^h9P-l9Ijl-OCI0d7zTYA&+l z*Y6}zYof%~zv&oRLGG+Fo_tUy{=zWL7Ioxp)bf0vzI~=G-RIqy= zz2En$pjwwiNkO%)6!=L2$H|kV!Y86`9h>&OO!iZpg4AdPk$;JN52hUnUjjs5F(AE! zvJpm4EGqEq=kwwW;xr~Opfte-2?)MnL~;t#XUgEXs+P5t_}IFp65ThdwPjP2Z~#{= z2l}VHHTAiTU)9v7nxE{x`)x3!YFw~#O)ELB1v6SlHEn7k2PRxOzisK>q2zc=>R9{o zMSGjuS1h`<@CEeg(t;|dqI3L?F~=TUeynYNW%Dgd@p0(hrE^xaH}74vyuJC>Ma2H< zECq=#aHEL1$eYr}?&8DaXNSE@rsPAvt=Hy<`BRpR-gV!u(e&5XzZB?uUC;!J1zx&7 z`Q5Fzes>O2Bx85v##B7ev7vmRA|FviQcYup2%D&wYDvOmDp?DkPBo>P*wcP@s@75O zNY%Ri1wq(r$}_>glfT!XaQQlzB?e2 zCx#EB!DujhD(FGA)>+X^!jqaqyC((UQoWj`+)}@NNvl6 zR^A2V`@5fg_SsYw>hf1>PpH)=ApRp~ZM7ft1Z%ZVgX{3IS1#|>)&^1c)7n~5rh=pt z3-No)aJvVo0;-Pe)*3xDK{gH2n8J%fj~6pPl-MIVkHHl1L}DdAPs~Gjb)P3dJdfcV zp~KQX4_Ar+INR6REdhJ<2WpniW!WVH;E z8#X_3aO2kfzw?H{C96y8fxI=tYjGKz`w&5A?e|(B?7^Bd`ez|RnS%icMF|7t1Hv3q zh{u(nK0|HEVc<@4&PhSvv_e2(q7t8I@wxMP`T1-iB@%(3>|cz_$3Y+ zZkRIXW;qzY>)5efH~tZREaQh&qrZqB=%?+kZre6v<~BOJXYrEZ?TgW?2bPu>84UOu zl`AbC7A_P&=1qepuDoV;-?5#$j=ggudJY6ufOl~^>Y1@^+pF8R5w!8MV> zh*J`DAVCz@*f^%@O?0CMqKSCyD>#kJ3)}Jz-B2^N$W1fP=^!Wd4ZlW`JfbY-^@DGe z{^J;T-`~nop~Cmj3;f51_OPYcS7a%IyWiC-OscTI%G0Fq{u7j~-TpqBwAr76%EMPBf_D|%LupDifIOO`dql`u{(^jd|*IYIx^%=U!>7yBr-47Ol zc@Jn!Ci>ADbj>qLFvIO&puv=9jiZ;)&On>b;5C`#dU^<0@WPiP(ba}A<8PkSpi%+a zuF+J9eWX?@_Ia|e+i(sog7@IoB19zDpEA&J)RQqF%{UUl?MJ$YnW!*;6O%Vjp1gS@ z{quNek)I`m?`CX zY04@_DTGP(Byqi&6pxsmOXAXZPF}x$GMcnWw5yep={8DLU_QQe0I&AHJg|tf>`8mX zGV>X`S#a*%(a_T{GX}gj;}Ozea?>R861C*4G@- zhW-T8O%{g`xo3(k--|pwtyrawaCHlinyNY~P&b4|2Fu!9_TYU?{>(HYQztLlM zXS)^7Ef4Mk`Lm6@GxyC4;pdyO_@!Q1uE8m_&sNyK2phNMsG?S%)U#IQ1G+-<&|!sK zz~#=71{$lB*%K}h1_9BRE&e7vp@xZHHjd^nj~&9H1fTFQ6ne)3%!tj~?n1{vp#^;k z&fqY}XWmIY?M72w=qnc}go9mRp9|<*cJsh1dyk{KIEaWj&(GgPXKMwPM)$JG*_y&p8DY%xvJzCY}QIyR;rbx zo&}!+Ij4|uDzG5AP9|HIlr_Eex=jAsTQWQ{KmXxNh2qN}lx*MkD%JOWD)(nUYGvGy zpGjoM1Q(*sKXMBFk6^7{F&yQ6FIDj0gLipF7Lt5xG=2+C%T%hA4t|Eu zAI5e8fs~@M{0ThOkRAFeVEW%SNqDs_(u55s)(=!sOsnQjFo#fc;#avQa*2G9EjZ;<2+8&q=@BuQPKx z5AmlgC|eT|E)b+;WD{4y8O1$w4hnwzh&?+X)*(i+2TN=YDquvgzsIkQ516u010XTu zNsgGj$MC<9ful*$5V?wk4f@EKEMbp0!ubw!ugd~p9w<25P^VC9T#@@TaTmLwYe7L`ijHUhI!FC)hA$^^2PjE)Wk8#F5X zI08b260F_26PnnTsJ+w$S6D7>DN-}cW?_ph1H&A4G@>hHXet!F4=&~}=FBWy0N z*o2uY0D@tUr2?Jilz@@j!n5;b8VE;sU$L&^mPlA*ER;Z+b*&k+AK5LJhsV*Yb2_;I z9cCDS>zZ(Tq~^x$m?&;oIA&3)!r}mcI9h02<@gk44GmIt~kvezZgb zd?f|MH5&m|C$yapw>TY*{c20kZQ8#t$bU5|I2n5 z`P}r}VY68|i(i_7EJx380lvoG z7aGu~&9fOLje8d(QOs*WA2vSw{BLN6&*sg$o#Um9gyCe&?epdV9k9)xzmMY?8ed1b z54XwJ=#z|&%)s|A6?B1rYYSkGQuNb}DGh?`2z)v+atYYtufKB^7(D69mYjy+%{4_G z=(>r3U9qynU0Ut_Z7+DY#+>XJvC_`ZPyGp4fKu=281L3x?45F`$Zwo^be>qk3>Z;e z%J8eNz$E*qUb6Yo-qVd~(%(FGHR;K{X2~>oK2^jrpAE zv+>v8!AHQwbwIEX7PO$_d@M?wB*HWq4U&S%*M_TPQpf#DaA)DZzv0vwPz_%)+S_Eyj-?UB` zGhQS69XBN61n5y45|PzRS^;$>6d_(g3jj$m2r0kbIWdt#d`BMGL>Plj2ejajo8PcO z8#fqP-HaJJ)~J8hZWudO9}hylq=bjO;kV3A1yWP$1aT#Kx3F(~wr0{Fg%}A( zdI4z`wG90PWU}A1j?u|XU4V}ezke@ze<1G!a@j?`e}WoD@RNSin^hCrQ9!iciG`_P zzTz=)wBWZ05LI_#zKE$@OepYTS&|w0^^e~rwJD+sTKdEjQW^(r(!Z(k%c|9XyD%Ls zS83o?(4?wKpMO(};41|2mA?B9Um=LE1oCqyrUYv^s@O1^zH4o{32a!$+aH?4qWoq zduTWM>gBF`zZ?R>hkJiG*1K;#V3eV(*(1hwPM`4fU(zytPMp^ylpJ$Ydd!(x2{r%^ zbOAOIl7T>G!x{5#IyQi56rCaMRE)4BA`AUjH~~G19{>IC=_n3;haPPOTD*9DeKlxH z-Nn55d-OO^rS77m-o7`DdB(msysRC zbP4)u1AzWRUH}zq*IrX7R1-<5M=*>1mFQ()_G-vQy@r$r4alafZ_DNya&gaR6 zf`p?Vz=P=B>v1L!m}jD`kiiRgvC;G{9+%Mp^La(DTGB;VesMRWq0bBkkiGAVOC~D! zFPqXj41^v#04#Tc({J3f_R87X8f8OkqO~=aH=?d?=!nI2tM0yM&9&1e)wh(iH<#rO zud5&0v8ZPCeXy_KmDT${1@eF1b;;B5Q0~$@%5Oe$JNn{Ii3NSVdi!+4P<35HJl2@g z*wN9LbM1;%+ovw5t&f%s5)-zaZ+{?SZxXAT1mQo66Ce>RNrWU?DhnUI zAx@ta7ktaIW;_9NCIfu!m#Y7;7j3@(`HuTKoFgOy@x^>#j@0j>6WU8IGv@p9InlG8$3E~Z0(A*-Lpql>2xaE>8+2n zH_w{0aWG1u8UMKPXV4+iJwjhoVm>!awNsO*1=K3)O6n%!ZzJd@o)hqY%+zuC7}O@r z5{{@{6Dvk87EgrY33Ht0h#{ARsP33?7fb|0L~EOLOOlI^5qtrB89Y&@i-qETN{f%8 z?j^2}AXS7~q$^MZjA0njIOaSxczWL3=(c&~&b+!C-`CZp{x;HNFPk>4%*A*3SZVn@ zblcmdb-MR&tjk;dsapLncf;Yb&Z3fuB}JWOha24gQma4p)E}-GSCqFPuV`Gw;d+!) zS4xTpeP#1N7o(k4W;c!W`#N}6nW@YdBsVFodk1s@)z*{fMRWkYcyjC3lb{lGg36PR zU1WgFs+YWV&|4fSyC-jq66ze4C7wgz=0l#+Qpb$$h3H@2gKtUdfpSdVJ!KI%p*?3z zPW!~xI~w%g$mQSY8}0x{K)AnXohT$tYPq9P|FvBHwZ8F=78tCDiZMC&mgbat4!)JT zAI&=CDXDbKUf4auQCjK=dT_?QIb#$M-x{x-1&uuKcKakd(*p1gSF_@q9MhRreZi_ph)aweN8Rc zIeJuQG;o>IxnxXaj)vAX#w>JTR(^v|d!(UO&AKglQq3j9Ee;u)YEOVo1!i**S{ae8 zGIo3nmvtB{?!sj>fX4&zil7C)=TF1~{#bnE1sJaqsu9maM+6LPt+0o=fLcMkdicD= zzXDBGBoZJaL-3?7AhWPWt;Z{)A6bUpwwBFrzN?bS9=*`PSneHh_2I(4=kmwH zsgu2)38`DgKk{NIT-i0Q0!(3`IC2e22S2-b7G}cyxrm>U`g`WoIeo75t5y0#=X+ z4#q(u0VCU9K@qu;n4}O3aRD1ffSn}TyCSd<*<=>LkBMRhCPL`uCBrMD)v=%Qf!)aB zVWKt$n;OGagSCr$z`ysR?{2GYFq&D`Z;X~reKgt9l6>@ed@7Nvg4y!gNqhgg{5GIs z3_Xi|4a3nkWHEW5-LUSv-#xyuvU8X(r+sk&9@yXSRkHznXGWE-j!#pU%rS%wYJSc3 z6@T43aW7s6_33qxAT_5IWfKHigjjA%+(c`gjALL-Q&j|o(#H{aO|yvBly)g2DB9xQ zCOVcO`{@Eu3=vg`jTF-YwbY~nI`!epu0FhFOL0eK#OpRFK|)V6tz$!enNep{XaOd& zDuxW5|nhM~>yJ>Fv| z*P5!8SA*Qj`h+oF-qtj|y__A{pe|7YmIX`xupoDd#*k%nL%`fT$Pg&VVJwoVdK1q= z27vr9t+B-e;gA!W0ECcMJX=j0vKtr~h!+4pLw8kUI`eq}C)|T+tF>^Y)+pr{*O zJQ?61L;8a-I73{*Pf$e&vK-M~F^iycT7gnE!Ny2-Zhd`jHf@cD?fLokaP*5}F$Eqh z36Ydg3Hs3;x)+_i)9mxuimL4$veXdt;R~SkrH4V;F}Uc;Wr{0#1IPW0 zydx3~hoWeTBQM|X$j<{`U6^nmb2B=%x2>6`<%|xlfA4kRz85&|-27>(X4#*{KE5!p z?OWjbcH6e^MEnxTS==4ZV`22CoP|Si+|%r&h`yM#s$z=P`gujIVF{9qQ~bPxs2s;U%19f5Mz- z)_HdYnY*U%33$NDz`*;azCnN1JJmAYgu(%u_DPaH^!f*Y9-<#O}NGCH3wut&Th zi$u;iguFbP%MK-S0l&aUkUm8X@H;{@h#RQE znA$OVVu4?13VUL_(HA3U`og>m_sVcN;-(UGp&lr>*Gl8M_4M_eI3b}@StrgV(#dmS zSbO3`Uk}+K9RMO11UL?$cnDcTFH87SgCd#+dzUhfJ1@Rt&+mPVw;h7w-qXE)6 zvv4||omk8Xv2mt%%QMfQAD@9}&%|{&xMkf$Fb5L2Hxfj9AOv$JLW&f5W{c8vXbj03 zbI7C=tKpCZC!RM}15}Kn{GttP9J5TOsJNAkml`hP94{dl#QwsRkEJdfH>&Cz2*0Ts zHSV&@9$p8(sUC>~<3?701J^waE*nTHr5;{azEZ2!t}I{oFfPJrSC(D&@MUEywcNPN z=o16!Ca#}%)ZuSkO|?+ts2P}hpeSM6SJ>ed1QUrkFcX|Tjevk~j**KJT=j?>@WSSC zT5HyXm(GE)xY&1v`7@MOT@j?}BDPD32#scdgA7I11qbrv2CGVuqxWtYWu>1g_`Z?n zYsVAZRP;9j%PPRBK5=_3ALAR($dxMj1er{3lXuGBS6CFCa=FYdn;^^5s|DbbF7<K-!j}4CKp$084w|1zSKMPRxLLb1-CP z0|^P2;E7SNIl=OrDUt~B0XP-7fqNmkmHp)&5VLUStgmY>-}O}teT+VieYI-nBo3Cjq;4%G}^0bPvlf+D(p$Du&<5-GZhJQswu7fnt*?+8K|w8OLiO)Zd2A+!-~ zOd(ygecNL|1*(Da(6;ud?p&Fm9VP9-6a6~y1H6l(B^OKG5wvgEU=ODLiz?tMm3$5a zGvz8>Nz1U-@<5=xby!OY8hft9D11qL;eNSa8W+JJXz!GzalrcLC7vJ}5kX%jK@cTG z%%C6IjqMM?-k>dLLwG_y#aZCL2)wNr#WVRm7Ow9&fjRbVnD97eky2lLhz-r2JYTo;_z96;Tlf$M|wn2O-sAnL|t3fBrn4uh9Snd<}1^KsqJ zz;yvZ_HR9_l>Afh+h?T81+PQ{Q4lWT>(a$y>LxD0d&bQX7p!LSsMm|ucL`b$`=|XS z@PhLN7ci&S0HZDuH_>y~Ke`_O2S2Xs9KU}3_|A17*A72(&&Z1034tw~QUyI59QF>@{g{P2iBwR@(%Enomm}-b2j?>p~b$e z!sueq1fUe42bV+&v;0dA0sHKoff75E)9{HQvt|uRHEZl8q|IjF^>A-mPD}74aL*Fl ziRt(RvB5VcfDU*#B7WuRf{q?CcV?fh!Of(|#TZ=7r$o#!tSWp2blXPuda@ZB^YKbns?YJMo*kSw%50^}xO<}koBF;&HLLR#f#t8aNgb(9wxYZg zT`sj}gVyq}j1IzEXr~6f++YFb0=3HpnlFpU9D$-;lH=>q`>HIdY;umqs8q|FA8Xg}8fj+kZ8je}!+_S{Jt zxlf<^{i`8^yhS60m>?+(gPHf&OL(36gEGOsUzFn{&$E57Q$9?$5}!5r>j_kzPJnrg zo%bU&tguPw(HXe&ARRn0hC)P=pAsxJSPEgH>D&(!dBKvPBzc-ru&-m9uDktIvb`Hn zq|#YT-O-d#kLs7l3%|Zvx>p1eW@^v$dfY+gy)%NYDpQ-pRdXm6_h$ib!Hws(5tuGZ zk6NQ4;l<2K+KMJY^!)@NFaiI{=OxaF1@arOEkZhvDHt41t~ch-7fiNuo5J}%FXg!NTGNPtw*J3{bLG+ zZnyjy$Uqxpo{{fX-C)Sd%gZvXjo`msdX>C&+_+Y`O1}$erE{m}RafWj(ktbgckI|K zSK>sC?ACqzZk3UOPrvcT)1)BLf)ng!gni6`QmGnh7&VfbPR*y*;K6x;PdMtoJQHk4 z5!EgdADA`}>rOjB2YVom3zEZ#UIchuI3e*w4;vV}Xd*qVWljtJk23W$=6EbV3Q4cG zl$;hM=PW+P=83h*fAG3+Laz^uT{JP31m~pp@T{2CE5K5V{06#9NTaFK6e%YmN8%Ch zEX95$A-H;jgnba`@e!Cj0v{k4L6MEg3Lv<@5hf6#WFfkAGWbH638aN4N@O(BF;V)J z-ZU0@^Q=LZNkBGaJ!7=cGN0ZrV}qNv%zmhQR?MORG{X$Psi6JC#aDNB&d|e=K!J{% zob6FYLwKlUJ!rXhumZPj4(&)S~YpNC3?pI@|IgTOR^!;J};%aL=Ij zHG2WrQ538UjcGEOn-^`o6<$-ES6t8(*MQz+o$1F1eebfGo0BaiKMUPSijUA6*e;W2 z$rCFJ{n}>J(4_D{j+D&$fSpyu%{jq_SHZ%<}*f(6);A8OBE z7^9&`G!ZW;1m0X6iADV-{X%_z#O!0lxfsXd>5$j#4S9otGzCwy#gUkx+FEQjnv9%- z_>1>R0#PE#@^Yg0V|>+;Xv7JGlhGU{P)r#%y9VGp2T6uGA@2MN`{rI4lxD2nh00UqpUOeS7$GU<76S0&p7wwf?~!|P9*{bsX& zE76%G<;b2pV4zS5g40J_PHUD%?Y3xKE|1IUaUF0vbvEK?#G!e#P;IuF4N8;8<|T!BDN>wVpsL17T6dGqbgCUp4q}Cg~+)V!_v(n{q%B3=yKIC!oYQ0WxHtTt< z+TidUb-6TlXDH-!sJEDvPA4fQUGH>iN<$%sQ{6^1h9RLyAwx5e#Dpg#Pd$6!0AlVR zjhkvVX_nFRK^3SRIUOBC?@pf%@<9HY`RE1o!aP!9&TL$w?>J5C3@VjDqf((VNXuD3 zT0zC;1ua%RZyB5A76Vqlm7JV_5uO5y?L(Aq$ur=G7>)BR7K3){Fu#8o`876Z4dLpr z!Qz!bMy^p<)E0w>1a)e&&Z4$*rYd`Ow!JE{J?zd3@g|K&nH9qITYQXz!4IfwbF zZXbFP-HQweNj$b--vje@&6~Fi!0QHgjvu`J?Wa~OUAp2au(f?|OLghgIvMb^CVrMC zT3Zv`&xuy}Q`BR7-|kkG%v{nu2|X5!jt8y(3g;Q*dbQSQ&kH2NzHF^ZqBI%odEwfs z?AAbCq^Kd-YM8lWX6i|(36I;c;hLf#e39IAo)nBZaRS{ZEA1?8E<=x9qiriJL62>L z{xizbwzg8{dweA1xW50}K}?aWF(2x{^mq_+qr<5Q)KThhcm`*I4ER9}m_|{2Gz1c4 zGRE^-z#KD|km)xP5KllnvC$B5>dyH>MqkLs`FOm_Ma>CdP&3{jo)AMECiKk-T+Qgy zMUCRc`i;1BcwsaPb3G>e6A`i(m^ea$q*sW{;LxORazRK5@u;*nDbG_@JdYbxm&W z%cgtV#BR7U>Utz$MlZTc-!V6S7LTAi!PrE}F=K`ML8+91x-$1Ym8pD-$*Qljcn8(p zTvU!ew;FA_I)Is0v%abJree&O{PnN9Z@dwGSr31jwQil)TO9G0gg376`-+QwUs-A| zyUb$^)TD}e@`1>mWtQtujE1{DXvgw9T&89%NKVQ%FEH^6&2%E zv!*lBu@=i2b66(xI^+2s<8+{LfqN`C?s3IrK8;DvO#>R>OkIlaT8i%q??vALP3qDy zKe1?IYZcwCO8E}^zi`=|%0!_*(r-l)?1M7T@)IKmMS#D{_D0_X@wO9!65uyq$spF?VB+!0C$w906K~nN=NB=uI{Ym=g6n{Ur7DJ+0L}Jgfs!Ns9sMfl{wE(PO58ST;#f z)Aq(8GY6GBD)o$N5D%W0vaJekULLC(#!5r^phJbD)LF2uwR)dHxJZYR`Q=4ygUChj zdO$AnfvQ;{6s_mssiABRo=KpB5Bs?#=h4;61I1a6K-9A`#|7pq7~{SEh!Edi5#!Mu ziJZSgDyQMpzX4Vv_kBx0{I&ZMSp?GDXB8@9<$!*C<9MiB8fy#eNo@&&kB~;>l->+3ySI*Lhd4Ghg(0S zYeZ2LGh1C7^aZ-=yx`ER!YpMDxKg9aDwNAN?Xs0>3wP~;m*j^B*T$rqclonMMypU> zL483%J^gS|WOCP{n#8=B722}Fxdt=)Gd!P5S~V!(lbvvlnf7T#omFL0+dSP_!BA6q zokeZdx~=-f*@0}}TeQ`(z9Ys}yB}h#Nfw{_^4KvXaum)Eet< zMQI&)k=(fueZIJ+cJq>CWges8 zW0|Znz(in52pU_Q_@}C7h#QH_<`Z7L%tX~*VygPGr3BUPdUq!PlvZ0YI%_r)l>+(C z56kV+Q8@54AL$rZ75eNsX=!_@bnSC7a0kwT2hrYFOIqgb+Bxr`tkD%(?aOLuyci{rJXL)lb-f-WySMLF=gEtWUdIPWDFbT}Z1w?zcbMIlobVM8373zQZs0^fC zGipKq+a)|fI-w`l1HbxWjQA=;Q$NuQa~|I^>88#irZ@AVJK+xpsuop&hEc!zq7SEE z4tx%O9=EJ!+JY!bqFV9AH#`HhQ_)`Lp03~e;{6!MY_ea@l^~i!#CM@Eh3Z7Kr(cT$ z4;~sG3CCvq3W@{7m+=9S5chH1#M29;E)LT)Fq}F8dW$$YdO^<7i}dO)(Sd^?a0Ia? zO&O>8FI-+#M(>3EZt8fMuK~ zXgU&I1OhokiI6U|lTc3Hs)5>48L=AtPdX^fx}i%~mA#3+1lrfVBWHJ%YL{y_4Y}r# zC$~3VBa^I<$oqaxM+F>R7-`GJKP47n%7)2Ou}&zCxkDuV54~zr%z*7rWS1mX&wR`oJS9FUG zPK!bi^F->${qDhAf&7-iwS1{WsbCeUn=O`*4ah=O%iA#ZKQYrp*U6xwSgBOWMs|`* zf>Pi(x*Cn^*V_{I^?YPck1}bAO^`tYh&-Qo1Ytuw@rs!i+7o{lG7thrN#l{pAJ37? z|0uV~=ceuo#9lv3)g}XQ!dx+J&PS8_UV^o~sa^?n1pPGWqd7S7k8+`GvKCOU$Aq#% z+MJIkpRN_k_NMj7kRXT5PW$NKsLWnFhzpJzOq7pk+7eylL^UHB-ZVEK9ojN=)w;(g z!gUpWPlvXS1PuD&FKeD#TFy0=R%^1=*1G0db0pNHrkZi7tJh38ygoS!HpI{T*s{Ph z_)qBjNq4-loQ;IMf%-`me$9FE(ENThJprLQB4B8W5SK72#31Q5f|trPV6hAGMxui$ zV#jgj967v#75T}E@r z;>&e8g6*ARrdNpMr_1CQwELYVQ<#+bWfdV8*XeGrC4Ldaf3@x1XQ&~iv0=Q!>)?Z( z@IOY9M5yDiTkIyambcm*POFvIs!ce-A*2c+P}?i!I&5O@1qE$ZyQ#Om8}y>u%&(i) zwvHSYbLLsH+~vU=TmEB29P@&_iY0Wo$4I{Wi|=p(wHkFosZ1fUOh}*hx5QD*SgMOqk_5My5p{+o zA>v)RAGAcY5y5L06xE@L6BH3`TOxqE5-F$817<>IIbH`pcdu(|{PPwh?$`MP0H63He zHJ2*rhZePsE&@uEi`igvn4626=vs--nQd3eCw#Nx_ksA7_VvRrcZ`@jF1+Z`uAZ-^ z)Wr69{b0{+0PL9i+U|+L>S;4BU%Dgy>eTj}$}G1zzhZ8aR(HvMhBoIY?D_2UVk0ot zpSKo_6=e2A_b^nF*}n3bFex1p@kk5;@-1HYOoHMnOWMe66zBd#KXkD$%(>`AaO(Gb z=JSVT3@rA?b-=(+3duc#qU~#;cIpggIARAQE2cJ?%R+;OCr8eFVjj&*dT`;>lMIT= zoF(Iz?%6-5`_clb&y?*?l(yu|-!tbtKL#fssF$k(4yaN9~_rE4NKcOZPz%b zRO86DvE@zI74Dq1Vn}iKQ!~JVCl+5~w=8TQ^5C+$_sm~moKilatTAN28h&!V!2_L^ z@roFtQR;lpyMD5rz+^wR*QU#%ar zzWw)^)qij1(ev&IQ2Npt8shr%9!8k|iHZk45$j6}rj7_I7yiyQL=+;?lCcqrVlp3i zIFp$XK>3O7f#460&<$C53dtfq$`T>6jFNtXQwYx{xTlTc(H}~O2;f>Y0#Bot!#>NA zx*?m79NE0|;X9w!mx09~3uR58Yh>9Yn=7jx)W}U5qfh_fq$5BID$yyl9i1B9REPHI zJujL2?m3K30q*dUnO6#`l^_Wo8~vfE80j$p#e|uML9!|9jQa@s`N;KOjjp*7Bsb6A z`67@Wv7kP4iCWUL?x6+jm$tN)vGxHhwFeA!tokLikxo@7?#|~kG zE+*&-{?lPdB@GUT0VWOLASs-p@F8iPEqesm!5CnFL^jt96a(bHPzjP|r_+p*u7U!1 zN!Z~CJ5m!;cO_%PhQ*TN5l-k{1YT}iURk-k4VBLl)`cr@-}@P_3k3vQfD(ti@a-@U zE#g>3Jp=_xFeC7Yf-H}TA(Amb7z0s>68C|SIDb?Cf#CEL=pa0ouun$(sd|4T;)l=q zfz;fWL&Eem!nWF`=M5?XLhO@vou zU6Igfkycz+Lab5z;zoswNkjzrBoUGvj}s$K4u&MYwCgoY%(nLudifI0jKD=bvUBNPRjf)O=l{r52=007PrgGJ=BHl23_GYizoTUnu)jJK* z+pHC*ZvFc$d+>KEMSoZtP%3j9$Byf8YB`Hm!#EnNvTDZ%Xy!_p)B{JvJMQ(ANLx#l z&WD`2@g<`tJ62aYv+wL^+w{ByN(!z|E^3pnu%_kTNda?+Jyzm8ye-9Jm$s%Cy)quw|EUkM>eecFQ4nKX(jrXWtXRD%RHF8@# zGzI?osQR8v`WsAjgrvtp#R;&`oiEWi;F#2{scT2GR-Gi@<;s`n&5}H@74UG{Sk|Ir z3tYWFQ&4-`XdWMB+FRXuEra0DT?O3T3|T?m3erAr`acTTcET=Ds_y zi6i@eXNy+77h9HP$+9F@xyX`igJs#6Vr;;eX1eL7n@)g$=p;ZwPk=zU5K;&!dY-#w-%u2RwxZHj3`~Bkw*6!@=?Ci|!%$qlF-upaI z6WM{D(kdBY5lRFpuAIJ3MICZ4hPU2> zqe)9idMC+ZL5CD*tn_WHwpgmy`6>+o#JW#NvKahEOVT97-3JWxpei4{=Bq-%w2D){ zs?}SXI?gw3+0w)oG;N`uTZnVP2iWebEH19}wHu9JFb|rnN z>*+0tz6)tIHDfJ8dkV1Q|B{>R3U|Ygc3%Yn_zD~VUjYHIhMskNX(Y7t`0=Go>(b-k zb=n=d2XX%tD5D?hia(CKgQ*jbaS%0vnnX2IbE$>Ya#Nd_@&<}LQI7%0zZFWEY39u77f}@L$ zsA3L)?f?>N3TWIS9@tGzlqZG()`D$nzZ%@7#dm*ivhgqLk|S=g5gxxA z9tX|Z?8sO^pI5!|vO-Ni0$068XTxvRx%88O4QZ^#2)tAQmZ>Y@2rx(-Y2m;~xRpht zWLF5jd+7AhM_3?!%(@?BefAl9_LPWOrjG8u2>*z_XJ&Ne7VvfU2;lr-0|SiWOPmPGhk8#Rf!?e~VsM;Fl=FeOt7ufWi<8O-lb zKe74XTrluGLwzMT>o%AQPmdmT9!xrWXXTg$(bI6{fH7blUDnYXOr`Zp$IVy{gYaXe zzNm7z=`5(7ckhNLW3)j`vHu{tznGHi1TQ~iha?B+{D{r=du>>`lZnSOc%h3J8NoRn zPrO5!{3d?d!S$=poc?0Zo-a1sZKkT{p)2EIsT=o8v_m7=;hh5$wE*-mP&)8D-+L~FjIvy&mWTJz&Zyy|C za&jGW=A<)Q*?SIFMTU8crqAXCKKdA%o5yzATa5dk%b{<&?gCg%Kw2TR#R|A9R{eOr zl^o!gR{b;_MhAH1)?seTcMo-BJoMe_nbO}Zm_9fUWWTyMvRk?N#4-94gVkz?I&eZ- zhmX-+lMc;x~%Y-3xxx=lMVHj_j=}v42cqZAt1zP$byS z2!7fO#8aD{_-f0e3Mn5|N|jTUR9~tF(dD6tGLNRlBkDYZnoZ587E#Nnm54%bL=<{E zqS1S){nRn)A{r4`^y4H)pWT41*GxTs0TZA2!!C&ue*oix{mKvD_ZkBKt&9Q|&Kog)MWkAKq7!fTs<;DFA zEJEXNJHdO%?y-iwm2qCojVxv~Cf?t6_;4Eo54YWae;a74$h&qauc9IkJeeD!e+uP- zC-W-67JTn8PS~>GFk908N^V6(E?13@zxfS1#`w@oM87Vh^B6?ExH#Mq-?cwa1kD&9 zkQKZ{P>B#pG0g#=u*nfuWfvasbNc|h=Yx+9k2tVmVe^cI%kLd_;J4@RpL%HoXS0Zv zhThZQ&ucb*z8R#PTYmBI&W)RnjhVi2?L_MgjXq8D$NS4>mluguhU8vPO*jSFQs%|? z-q>~M{lK{88#XQ<7kGaEp_gjQ*;JiDndEDnv-rbJXMuXu)`uV2I%?&#iD9QzuN|zv z|GYETX;A4>`qXs1=1f(^cvP}zj}RwyK@ec#G8HR}m*FgS(2J!O#D^~lM86hv$OTpMcWucX-vORWV(!IBB9z%> zbkZl^6T~L!WR;BN0ejNyV!G#o1JOjqa;6nhNls=3pPD397hsG&v(j75G657+Xw!^N z-qnR`kLxYy;|~*hn<}nGPduQRfUzh5{?j^hl&e^`8@+ZnVls7r!qC`MboYN;Yuzs3 z#5dr_yL2e$8@6t>KXXAg{1 zU@y8r&xaSlRWLr-6#W;1BeCFb1~4b}$-*m9#n%(w1o>AvLW8 zVXd7F+Zif4gWeyBFf8%65&4GRPXZu39a7qSO@z|xSxS?yr73L3i7Lr|kLIEp>K?@D zQydn{^KJq~{p*K-U>y5T56;9y8U}BhYrNRar~yNOVjm5RrYrTodL=M8IUk;8cpdu4 z;W5L8Y5m$^!%+C29&n;xyFaWwFCkUv1C8E#GAwKZg-=@bnh$h|IsNMEKnP$HABg&k zkfH9M{eI={ZTN0OgHG2F0!~n7E|->p9Bdp8FP2Hm&G1e5u@>EI_|;5UvjDjnAAelj zmrEaNDMi_Js3mnO0Afxc(__9M1vico?0_0;XE7)s77U|1#~u@KdoiIEh%LrvF%}V! z7C?Ypjl7q)GIXe^2{%Nz2~adG9ocUZZ{a8P8!07vx-#^~$T@{fqctfqJUXdDCYLFs zI!}heq}9k2oSc!7RN#SKw?+2dwo8)g8R{GJp^<+515MuyTds9Z?>W|7TSi~a2e0!f zA2w8s&Q^oga0r`7g~D_ZON(_htrOF%R>JT+YZsfvdS1@5$&U2ojLjN+=}PXO@&^2X|yUgF$EZj$n3aN#@WYpWD|QxjVLR5Jj}C z4son4*xE%&W2*`m*(f0*P)CB`+tq0kZlz6jFP4M`$X+|{?lGYRV%1G}uL*Im0lVNL zorv2rf&V5MyErPZUib2h-+Zr@4;j+GX`VCX2GzGy3|?24wDMVE4i+A~X-aM?O)VPn zsnx}?uB514-*2HVWg5QuUyIi7xci-J7ZyEbf^RzXTFvhK+zqe1!i9nOmF_Zk@b?*~ zw$$;mFOSTBtN-l!FW05GcXjYlM5K2$}DXvGpBKE zuDSp6#Z@ruGKT~cC)9eiJ`ncRHW6P}71PSo(#oe*6b|t_`~(b3w;g@| z6d?F=(V2_@&3PD@R>aHDjDU9&>@kc;+7x840G$GboRnpvJGI5y=nhT|78o5|zt=?R zMnk%2SBaK(&wzK&7dv!$vbDbxIdapv#c=ct*cMznzdj?Qe*W5E8>A_bgkhtPXtneh zTAN}3$P|sjC*H2c18CxXmepq9y(08u!|?Luwl2^ZA-L~vYvr=7pKm-4 zvY&`hLXX3HKTPW<@I};@5|Rq)M6CJ=pgp+h>s>0{F8F7yu$zOQO56vwYW5ra1 zP!e7gFEkU}c@j0MfY?A@D+DjY%O`gps}SileGTH=*6&(##i`{Qov0%EU{@vB-wl9& zc^J3yhJ;5+a6=O4|H;F^FrewAIz>Ng-MU%&6!poDD+yI1{ejFiRn$Pd=Nwabk5>bO z$Nh`?;V$B*FcEO#@g1)eOJSS&_}5r{tNQKz+d8=#*xp@wrIEU^NvVx)PWU#cv!Jg- zy3D2Xx21RXp(e`)Jzd!NL*y%1sW`q(|{rrM)N0OOGHq<_HX+VC<&8gBCf@Y?Nj$kQ1X zEi&lfAENK92Xof1hkM{JrN_Q#d$?3+a>S6csv$#EFalzU4JMVRrAFrr3Z2#e`8Y1%Xp}t**kD27h|~19-I0lJmRk#gaR}*u3=P(WL(*rt6jd+%6IcDfWSn&|f6{ z=`jW<-}Qa688sx+iW(3_z@JbA+mzVXCjJn94o1wWADt4-IQr?b&41pj62@RCG1b6{ zl0_&E9?`p!+aD%}Mj$91xqKJA9^nxegkmgdAHdTn2DPCmwy!Y|wc$9b`B&Ny z^_hQ*FcEhnLQ|5yM_9dpOO1P9XP;A}E*I|6gf{q(XFq#s$<~|3?7{1|o05UzrM8!L zJ@IyIR8nCK6@aREIJW{E3UdKCgbbO=?C7CEJH|pI--`5aLf<{3r7)eS;s_^BRwcm~KY1Abd6!PL>+4Mif%XZt@Y#-y6P|fnr+Zt-XxuS!qa)mX9zrWR zKFqF;*M*><3#CpVmm&)5@d@0P(d6~TH$m-jFsk^s;pggf@FPizBu^@R5q=b-@&BZZ z!1bb3nuij1gu1Fk&qWo69|<>J6sRDYhn@i0o$Vt;z9_sU^8HQoD)}~8J|ysvoj`CD zUJ)Rcx04OP>>?=%dO_^tNBM--B@ANpKB5yo70*<$UJ`w`$2$>$4YL?e7=yRRm{F>; zJ7X;`3SRHzBR6;TR&)Xhb0+QUibp3Z0f#Lk!Pln78^DUM-T+Z0!~nxyO($^NV~(OC z2fXbq>sR^JD=HRkIeO+y)Q;o0aFL_^xTA<3_U)dM67YM;kzJ2{8+{zz80jdYV(;QG zeXGMeVR&7@8i~`;CXNl010GkWDwjQQ-!-+R%90uy+u7;&2 zW>jxVm1fAS#_S@eQliQk!`qtc%c~p5gaQ*P3R4sxKXnHFJvlYmYNS=(Avs3ou{o#i zYA)Ugk2Jk-eC?o6iFl$?f|B2IcJZQNI2jJ2|P*sh_$s`g;Tu%eO8OJ?Rjei}yK z%55mfkyyqss)pHf<8tX0sO>hP^+XUOmQVsR3DG?#>+FEwj?7535doEh46RpbqecJ z<6oG7(%egKu(o)J7E(rSSYSv~UB}LSM}ozjgDqz$n@f#x1wo93P0%8V&ja?j_6Tus zZiow$IB$FfgEdmIXS|8<_0KUnKOF*13Y|^?kLVPw3LQLxFF+Hyh}!Ck0aZN%i-vfE z&EIcYxlTXio~Q2_qStL0@mX;l9gYF~!~1W3TF5urT3q)-(Ve&XrY)H|u}`L^9R1TY z)fLBeqWOQ2`gy653H8H0Q3V9F3;_$!S6o4c7)DzqG97%x{gvYh+(KeSjW$wE!hChr z^V#bX$rg!1DY<@KqEw(D4)lnL8lH7JhZ#)WDtrJ8JfPQEQY~g@XMLle{qsz^VxD#S zea>M_SLIi%(1=nzcE2-0FIG#L3H>6hlAxy_`-JhXXYbUc0h9>M?>DG+M97H{hz{+$ zuy5Z5Zsh0pM?>fmBcX)=Ci4XA3>xv>eWCk5N8xZ6mM*4aMxy1ycnx;mZm>&mUw7Mm zUWTZ==+Laz+6sRNfEqXr9z_4AftmpPp|urIpbuC9`ao*VB@qQft>M;4D}zs}WHp)fb=XKz!Mc z#EBEi8PWQeH%7wiUf|wQWoD}0;a*tBgg3t2-b#Enf%6#NsS|H5;oUicG~(9prxV^! z{mZg^A^0o}McWuCxHJu6E0kLnOK|lHUdP3XCSJt%YVJgIXesf(Vj-9}8Ztq|+<9Xm ziP0pXu@8B-6VKHWAVkt5l9M!Qm~Tkc>y%b-g9*{b=%3lymI4#(PbWujj z`092|PfYc8st1xfdtA_dOQMF~5Q!h;Zp7@A^QmfT5ETI;pam(wiRgT9&>sv16Tlp> z4Ez^(9b5)i0i+e^^I@bk7r{w0a#-4pJu$moq5ugKr)DA{4OT$#8-X{SkAdsBW80a< zF0|C*gR~U@BjTNnLXNDHIH|_i?Raq!I~EJ;Tazy~?cu#p#Kz&NE(oyr$6Xxo#GXT| zKE0JOVSptUPcW7|tUCk4ECswl23vQT1d%G>4Oj~ml^7@T27#5_AtGWz7+KJz1SaA05QSa*6k-yL1a8WK%4A}Ri+T}x#$hOO;%f1Jp8%JK zeL$kDIKO}ms~3t1J{7yP$vzr1q@YR_^DbSo575I>jK)&MsPw#nn+r1Y+ZQTE3PBJ3 zHpp_Mr2AdP7OrJTeM?K*l)tS?nScAzq4ZB;9S_Ea{RNH2=+NlzOrr`%z6@wiCl)0u zQ+SEYl4@0$EDp0)FXMfUGKoYrm`-a(9$faN@c1B!37qZL975qK)JsjXewhE zn&r8a!h)jA75U}Uciy4TF182d^f2I?+GTk#L@aOgNqL~xnjIFC(r!+XNyQe03H~f;u(Bx@y=|}~S<%O;;FuDxYM@n_ zEi)L^*6XiX8zgp}B_%VpT9NExUUgQfO3N@(uJ7xNa|19vbOIO-+8ID=s#N9@ zZyLw)Qd%V8vfWY?4w37?mnpDM_Q%^7sDhO}dF| zT%PUft6`)gz5aDu)lOcLtTR?|tk;kbZcM3^C>(arT#g%&o)BiMRN}l8M^TPRH*n_6 zJu^R=o7bmzjVN<&`xRN5NmH_*A5G_HCnskW(9FSMMs1o*Dlw*}N~B7?GF2?Mpiic% zp{0F&uAHD<yL>9Tk zqSh)TQj66fW}Zw`SmwNg{LYCenFa`bG*?b@!>@?!n^-ZZ`b*y1I}jxAXXU8p0bEJcG##ti8565H5_ znq5DE2f=N*0tCZ<)kOfQZ)WOfrRRSfBK> z2E*<`hmm0nmfm5I@2_&%!JsbgbM)%N@x{Lm!w=p?SN_vl)0 zrb)?3O}6}!0Yj(FsXR2syLjUCq4mAJX=;X6TZ_E|dkqf^jq4o5{BorcRM1*#2KMGc zb@x<+5goh1H0z2GD}wlTG|zikvRLFh#R*vXhPJWVxXrW9An4o)AlHcNk6*cLqMlfY zY!-Y1zW3RN4WEHx&;W{YC_49Mr00cdwN0%CD`(X@QpplO)iG4CY>t~se?X$wzqFp5 z&%rC_m?oDw5{?6^bFCXbgYWft+wX3H3mqM-hWK4=>QJrEQKngl9^e7@K4n?=t`g#;0+SI*_!1jMp9tJIK z|9>hEjX2W(v+~fLgOybeR74!UV zV&@X~AM4(h>XS|;7syV*Gdi*&RNw&8I;}O)&|Z{OAr7g00~&2!%rM$CeiOV<-ed;V^7P zXLU;pP=~m18*B<(&q8E{zVq6%ah@`!HEh&G+I$9i9g+#!8$$@`*njDjaV4&pdfZ`8|Em0v3jvcMTCAG!Wp92 z2uj6-v2)ZY>cKZqdh82Wc#5S!+&^wR7W$(I!RG@GMJdvQ!Zhwh_yJ15&OsGJbxP}$ z5qV=iEJk&&Rrk7S9Pt{0#9BHGUZ=gQs@Qw59sN*0^Vwrrq1CugLh6cZg8qb}Ggx$l zHJ(tdqg1#ZMRMrZfo`BG2!1JWMEntkz!(e9;vY@UFyM}FU5HF}+-rH3iZo#W6fTrmLR=Js+f_v`6g2=FY!YHiG9yhT0~%1I zib}M#5fQ)26m|kv0sPLm^aImw>~OK0rO@(gsqz=)@F!sFKpndToXNDjU}?&XQ1Mp- z>Y5a#IK-e10c@Ei%n@|22_?#m6$1BDQ38He68ff<)NpDlvAXO8B=mQNjb0;1oTZ>K zX~5tRHm48ceHWAUB6fG>B9_bnV!GxNJZ@t@q#FCprcV6*X(q9B|9+|1q_CP8`PQwB z4467*ep%ON&TYOeS=nF!{mztWb5^XFGi^#iv&FLJ`N_Gtlb>HRjj0(~RT^rjLhK|g z1%DYhu{%Ujaj}!5x6#~_Md>V93)nVL4BsoO>D8iA17KfJ%!?<#G+E4hTjVO57G>5q zEpDpM6tQ>t`*Mu9k0(&Ypmlc*>j2_2-A0 z9)KUd^cej3__RmAV?^C?u$XSV8saUv9<==?{Ah!t%Ye;DaQnKjslqx%M=O?YvLS^o zJfW(Cka`wP2WafX?;SZ3k8HxpV$tlNuEY~S@W_$)op3BJ=I>REX*bqo^-<;22x=~t z#b7BN#*x=_%6~hhzG(T~c|lOd<4M@KOiS2tA&Q0mB9oQndPay^5$&X|V+u-vXO$J1 zG~vS9$?QfqWmYJmfy`ikF-%@H*#Q1Rwht?+^7E_m*&XBW+Pz`-UE}*LoZ8H4>$Gh1 z)P?;zs9VLdA?$r28e+mI%l4nU;E6aHdMOE&_U~Ux0_uF6ePmM2;wrnnYH^Kh+xySG z#M|xsOV7Q(O?J!JL>XruH3;=uHO(8fag~QI7hGy>z(s2kHu1@A5M+FIG^R~fY;mV# z40hDD-5!*L3tv2PVev5Vt(wR&;e8tAExG?O1^JmS1 z^I=By3lO3B* z({2Z<-@mL@TZED@KS-(;8IjO;T`r8v-s?Xr zJA-<=1C4`!r|2V?kt0g|&(HXJ#`FGvzvSnhembJu{&sfu+uOVMr~d!D{v_h^*&Mi4 z9M+YIKa`+5L7`cE7Wyt^w>RceUE>x4sMIFBPef=uDtbWYj{%MeY2ArIcMcg`MaGG?PAv8eV8gY(@c4p0RUSCZdIF!@@*VJ!y87;8^o;sgl!5xb9h{p zt!iA=0awUZi&b$$^i%16zK*LB;%(1tS(K(TP1!#49&w%W_My@G-g7fx*t>7m;G*qQ zOu95KT;++j&}wWR8vXGGb=F(!%SnfnH#Z&ZwWWZch~4Oq@dWe^&+Glm+3iy_qHQyw zGBXFx8PXicr>W|Zv-YKfr>AUZ%j5e%f)20?&7uRT$=HuEhu2qvm?dBrRK`1zrn#89 z63>Yk%zp~-MR-GobQzu_7`-?u2pDG^mYOrfFh>G-dy*k{1si`p=DVUCc!_Bw7W8mz z;mM;FreF;RJ7(?MH)}!ez_I&gdGhGRXaMhN?(Ty}tr=AwvmP`QR)7!=!A~vP z9JRWlNUsG=){JkXOOuSg+B_$%jFJ^8ZMy22Kc}Gv49oGOCFpxwGH|<>7WehI;5*^% zg+9)@q_0c5@4`NfWqtjueVV`Sn-!hfxYaPiM8DO4pfX_hR7np=>x*tsD6l~xHXEGA zqLAc>GQeoAiEDkCRmwA=+F7-;-mJ)(9-(w2WPNk#`+T*l?S=4?C)m$({(Qe&@lap( z0L}K!zDL%B83Z2>^(4^g#IGDUJDC;y5!^x;Xo^wSA}klin8o0R273%O$!jNC6|q$T z9@emk55x5>@QdiD^(~Js0}p0L8>a3SSGLrPTE|C!>kdUK z%`Qf*k$TgZP^1-w#RKx_@Yu`}E+j2VgMF(eps`%2R)F%PRIF5Pc8REx!pPt5KLZb8 zk1r?hZmG8|do;Xx%8(hh`j+dhV9KF2jH1|OwmCfdG?&d~&Q<1?m1L?^t*OolRW`GW zKdkViyg>w50wx~j?TV5oA!MlTQ(@j%wi}_XKHS0$WTc;m3L%(j==#9#8 z%lVbkfUzLGFnQ*_(jv%Jk0^ANOCDUaQ&R3K2r(PXQzSuGeigHrXT?*+#di9+>~zpk zQd^9M>e$8V92m@{K2d=Q)%I%Cl&>7C<~ z9FXF3)K-~n&&*(p3vTd=!UeAANP3K`pekRbh<*a@b$Y8jN;yooEVjb=wk$JPnbW7Z z#{Bi4SReoVa)XcGC#M*2d`6S^NH~**B|xy+wlvRf?hSl9%iO<-q=d zqIyJ|s-84D4Q8=ogS5(nqK`;I9hKs1({n1`L{zCZbVgZ~>8oWexqW3LblWupvVB9v zx&6+c_w);T;H5(Q>RKOjo2laH$qD1&<0I$nL%b5bIL|X{-`Ih<3os#u9b8Qy!+P{! zMImU=n>|&V)#@Cr1%8Ud8CKAw)fZKO8OEgO(!TROS7{TbyU{SMbmrBz|HYpJhSfBT zh3~jLeTz%+te3F`zUQm$#DU?TVJRw^@Q;RDYwi>oIh~Owv2Gd0^-4!4;@HRS^63QN zP#xKn)(My}qjd`Sp;ob3p@V-^=(I{ES)pTC)WInq`TjE-Fmg(I)!HBTWOK4YZwxpV3F?Bhe;w4cegX zG_W_pFx`fQocIPwhNIJPqF6Hg*yl|kOm&kR;diTXfV=ddwK<0+H`KNv=jRDn0q zqyLSvJB6}C4>p49x9F5uR((Z6aT%zbI?59Bve}m!hI(kYyH|ktt|}K(FY^;8!o*h! zNrkC?Ml9qN)a;dj0I&fJ%~fQj4aGq^uF0#jD~WnKmIh*t4zx5U@Wr%`sLj}k^K*J@ zz~v4E+^zt-E-*L{7#wjgII;l!v1=F94_Ub2NTl!4MT?I<`1MhC-OJ;k5(vB*9!TcQ3f_i#Bj4og%zGK;yUjC*XH3SO7>FTFHx#0`&X(D9i+_foj#o z_KT}n+5CB94_sKX=>2;qM0p&IJ_C9!%X-&%?|JDycx`{nl#-Rk+niGt><8leUb+Xx zPhHT0`ponj6nlWsMIF``CSZ-|V9<9d=Kw3f9?5xAO!*zHK4Z$|0jzc8VFW!SD~o6; zRxGjtrZ?OIe*sdk97y557uK(TVLixIu!_t)_o6d3KxVbd(?+KCIRk%A8;OExKsMmr zh3>pelth|Q5VCXnssSyfV;^$5?4g1TdI^xe{0hqHmsef}2iK1uw|@P&@zIA<@-njQ z$u))nBo~F%T73ro-HHMuaejuHWP4UdUW(qT)S6kP!)){>C!4iOYXW{4Px+}J(N>M` z+IxVASJLUOd=kQ%M<%Q!gq>ue85LckqrW(x#{4g>cG*N~qwOZ~@%`gBj32)Nc%>P= z(xk3c>z1aZr1i>>8Z-M0yW4wLq0uNYmK#qk9E6S%qw!Sn_Thap`@aVN{@QCmPOnIW zI%OcvX?*k-eG-=}PRh*CYLmGneO|9zpR)L_f>;KN>Vzy`D^~h)djTzwzlL)I-*(40 z6=V=Epn7Wszjb(#Lo}fgIfywg@8rlOppz99rB;sF@)bP&l!G3+Vptp~Y%5xIHiJBctxaRM$}&^zLJ@ z&#}#`NUEL)LKk=If(z{z6<_h-MP>h9X7C;WTZ7S`>@(=+3!^tS0su}k`ge*JjpSV7 zBHB{s=oQ&9wHzGGc7rc{ed!{QPkTK5{#yOv-asMEXNUkOq=QAUpFIjS%yn0x5+JIQ z%Wm%o)h6I+OQ|GkA>wLxB~U!P@>H@s2(nH+kFl{)`=eTtRY4lrZpDB&1Tq`ZE3#fv zVLm^AF$vK{KJn~_Io*7+E)Ws-ZC30L7!BnLG%y7XkHi_f+ibu*Yfm=2(u+{G6C_JE zZJo%#qx|v>+a}O=HZzuFR?%zVC+pRSArJxefPrs44w7^VG)U+Lhtv8>Wn8s#E^SX? z70G)2ptcPvT7lB3`d7U7q+2d?&flL_B9*bF$`NZmgqPq;@Y08C)_e#uK|hfB;b*s) zVCeN`7cP!{7~NMqch$PFqUbC9yp`+6_I~>~tyL+c=`DwBeNdLws+qLY$|_PbncB}c zs2DkZ?SMY#9tTFXT%?oBTMk%JI<87Fw?v`{)qc88PU9*l27E(az9z9i^xA*MM}gSf zYNXOJIu5`)YfcyXT>cCRFtP#0g=P}9)2O8p#c%>Y?asjXB#5vuxBvKuZtM|lAPek+r{E{iVH=h7{Pmz>spuqr2#+fo_b={kvYTL|+%6g| zteGGdQ3UW9Vu;Qs&70gJD>ekeSQ|vy{$AD*?-FhF`(HbIP>+ z?wui%EmUNGzu3Q?Pp>J19yU0V-^gT5eVJp4w+mA zxGX1z;~xEQ@`6)mQKU|pLVc6MT=(_@qid%F{lV9d-3HG-nyP#f{_e|7xNkhiJOT>Ag9o-WFTG>wfw$f~ux#_P*_-d- zEc14)8Q;D=dwcu%HM{1`Sq{W|egM@cpTj)~EQ?%gg^#VS7+wMKxBSc z!4=raq81Uwjrz!^N51l zY5ismpR?<>cl&y;zd32-qI*_6@0kp)(U-VOcklQkJ*uQ&*Bj%9-~acG!xjU6(UIPd zg63a_!0*w7GZ8E?2PRi7KK>kdYS`p{`H#-u+_7rp_+bM+-E@{7c-L#M#pP^aUhp%5 zaRF|*t7*7tztESsF-_?d*U65hNZ8Gc+5p*zh>(p4&=j@d4NFm|Y67q^Bw+;aXEJ9a zg8oZwF$1T(Wr8| z?tG(PNrp$sBx!Xl?X{Lpgg+KkSF_)OVst8a`hptf(E98_ft7W(?DBMnL8{e{=$$vH z)a%fI3)NgWG@@kb#@UA^j@C(j82earbpe-zA8h}&p!x$aWm?|AeuZ*#RZ8`1M~|Kv z?8*u$67u!unQugW_%@@{)ekW7HdHR^3k<$~1;&hUU&q4Arc{MSMD?ybVMW%r`?6KgBNfSeF6E4vj61P_DGwQMB zTMQ=#mw_?rJBx}_6U}xq5K)a5>^gAt*u8t^F9>GK*ij%6;v{qbIrM7AnBEGUxYfS-fdGdzVfB4gf^$j^HASo`AI(q|V z%FI2x&%eK`%x_Vt(Q3~nYu+)SfAj4Ap?Mpcp59cmecM}Sw)v81vD9ufq!~2KT&p#5 z5oE6N%w2KYhxJ4AJZTb{%&d^`v!;djY+Re7MWj!$?$HPDy+bBi5DbMXT3U9^7-?Bht`i9SKrWV z=TkIl%am#`jNZ~Tc z3kY8x4HPFaK(sOjpeM!%{&JvXL@Je0r3kLw|Jl-IKRk16YPy&eNflh{9Iz1_cn#bu z)9BN^8m+{Tui*@KbFMB2h?HUpC&K!_qFF_rRd7R!)1_4WDRZz+CsVqXZP~HDIatzo z`|@p5iVW$aM26nQy|wV8+%c<9PM`X~q{`%IQ@^U3;Z|j@=DC%Px+V{k+WF|ia* zHxeB%C4|{!nPZhpptDzWhB%Vea z{eY!fZ>qBp9(?PDs_Wh-+=z1_eZtuVapodaxzqPh%nsdT)c>Eg!zgTJ{>m$Yjrpsu z3RdUw>sMZpL~Q?A)7*3G>^iSu+yAb;^k^NGNtIx%Scw3d6lZ)%K=05UblPYKcq&}w$kNg7l9 z=rUg?dh#O5WsYnFk1JhfD4aTkcytuximb5qAznwQqClsdJPv-~Bs(RYA|pR|Z9|Zl zeGUhYfLwS1Ho^-ug)6h`oYta!6tt?M3-BxGyV*kFHpm5!)S-LlcHv~p9u;JoPV}8W zCUcaN=-?0$RF}A=>tkW0rg*WssA&wi0ke??(fd;Ac1vbEu{Whdf>kP&X^Ff71QS(; z;H0&;W?HtBlr(Bv_K)bRZ?|ATNP-0BGKVZ3SBQ?knQ0XO!ccOYrnOa&w~HyRgXk6G zu}lej$vhCbom^aF+8;pN7w7bI8cyRx{{cGlUs{aXXgDb;dT;bzsZyswmo&Pho9Sj- zM-muvlEN+$c|7fz>DTNpiVo>z_Luf3`^)7H zX`*acgG%L#&o_9Zmb4@)kNp-g@r`gitZ=buN}e>;L&HxnP5YHapud(rXm}C1I6NMFGdw5id zp9Sqsw}=xFQ_Mh+4`3w;tm;V%j#I$9-A_Nlsehk0?Qz&%oG#ZhY!c^G+Er$yire+@ zkKjJ=Ex3=aO@Q?j{(uKQ2roaTeY`}<0HsW2~THYO4)HHTz#T=JNy!AVv{SIz@0yT#C$v#RkqBE?TRUx)e>@$^k24s!~ zqJ8VWKQV3EiSNmGl&}={57Yxil$26nDy>0(AQ_M|HsgipKTUpUz>Nm(=t+2qSr$DB zGTFm8Ob>yVaV(J=Hr!|xJ918d&pbCiUCL8X_ zyi+V$yA^&u^7?OnGh(Y5+#wTpu46?4E`yXHYuf>%v!f0yqS`68{F6_jn?Csjl%t7( z0>|iOAPfF6dIvlo@7M8XwNxcFBKAB_Ft-ElfEzp7=FmzvfYp>^pdi==3$39Hb{|@G zVvQYdz>$tQ>Ea*_d_+mlr?I1zTr3?f2eVCHo0dF#c5+&+e4@|hgZpgB;0Z_7fWnO% zn(FjYMGa`(E8=JXPPx7ju`DA`p_lr3j)vcxhMDBbez^E-t9{tQ8F)OCd%sqQ%pUydK`Al+coq zLfxkl8ie1L4o zaoLDri`yRF%pFF9oVM)ckQd*)=GeezuD3?*efiP2YPx%t~4S7i;Y?4`JQfYQ(X0}u+ zO_SvmNhC$r@XJQ6B7M5=4O;XvYL@~meF!pm8wzVW*sToe)Ebc-v3?koD4+zq-S1)Z z(F&?BP>w-4zlRTOfAwdY`SK41z18$eu`M{Hq1tHN zeErP>^jE9Dd3W!~KfL+!jaTL$ZLpd9c;V*2K-ymentt~a7(Ti8`U!(p4=ORM0N{qK zyC>dXiEh1sMxR1asHeqP3fv*F5lJVr~ojb1Wn)lYu5x32`{n6Id7vM*TdY~*mr2D}mQTS08t%N^c zg^P~>VorkE$%g9D7Q@qx;SmJvz^wskh|bY=!0nD67{`oifA$6Te*Ny~cVHZpM;--J znOYQe`N>8rB@1T2BwDhGC> z$;uJFJ`VCGtRzuCy-sS}9lT( zC%4Qt+b}tZD;=C{n60s)d^Bp0lO1DI(;tgn;#Q88YQtr-of$z}hPo-9xmMYvPw~6z z+*!WTn)Kmw_FdRFXLx!|sV~c2=kllMOZ%g*(!W%lVGCwBXP1SwdRcef03MBEJK;%) z@(ZQLHb7ny>Y>!KdPqq$S_0_j*TW&tMAy-qZ>6mgY#9s`@E?GEArb}(F!L6hCzys@ zM&HGaxZyHt5H*STAa;x5_)T~pOORC?O_ohuCjK0(amf7rZ{OAN=SP1$ zvo{EWzx@jsYg)X&eUd3FNoSU8`}fz%iz~E~0JX`KWzv}y+BtKy3bQ$=1<&=GXvoV? zvM|z8YySZ&-(RuoHp^gBDA!oK_rl)!gYP=?*GKn%X?)>J_}g!iU%u_h9d?DL!rTn# zW^*t@VZN&xCcTxe&<4#9zW&<>%oQ4~JO%L-88;~I3fYIBhuBCm>*28~;4)$l2pl$l z!Gbibo|^`UPg2&6x8Hqn5gWnya%2M!ODw*KS5qrvvWmGYtDjl3=9$%37ag?kx;poT zm6QDrxx|t;Y*s^Vir8eCPuWEEUtEXg3UDc~c)!jb6rXXD>r4^&stQkFK&6-oHCzlQk4bJW}a(IJRsmrhQ zW;pVDxs~bpDOMUxZ!qWOx{C7B6?|aK!aF7m-m!jCX>r4>nO;v#PO4O@b@@m6)j9xz zgPln(e?hO*8~=(u8s5~B-CUT55_15pzt&bawGY#y zeg0|d1QKmE|5a#EQHpb2{FM>(l-#B1n?K{J6@2Z(_uTHJyXeCN5yh=oIfCp^+d zLfCIJiav2LI$i4ZaH>wnI7H(|ULQV^$w&qiSv27Tm7D?ByNX?iMx!H!;|jyKEJlOD zXaS{6|HyTQPqHU^+_eAZ1||5Oz!WMTzW?*jV|I4_2BzcCLO zXzp?|9>ft5HEUIMa_wI$u4@Eac|-^CZ3Tn8V2hM0yO@K zwIv#)1Z9({*|T@=p7r27JO_$k!Hw}C1Y5^bH|XDo<{v-(%jx6uL-7Fk)1JM|w!M2I zlfZdUg#Mq89-?lHho|5v^Z;l|<+7!F<9!^)skmPkREe`D0s@JxoPHxs~IdpnC7ERM1wbJtPyQl+-9AV_Ar70GnWV^lS|vXXoTK-^=b}Hp35(to z7jXsCc%?RSACp8b#Y`|Fp_eLh44^n75si)BM^80HH^TP}Ig03=%s?FXJL&|G@t2-CND>*niCpz+$CwJ?)l z8-%BfhS3*RoGa7S>B`QncmYO7Px%oX0$+neKhmvj(F@};XfUz1seTdwx3{&vd~Euf zL!ZuU1fX%|r-#-|Klbwb!ekJ~ZivfIgmspV%0&EtVDoKo_;kb*nZ4^rME$_c6XTQE z6o*!39Qx~_w?{LPNQC(bJ_bf$wcKbETrOrWiP4hnML3Jz`UyIG zF*4YZ85}t>$X*JLq!)z4)QvT3AVxo+gmC0R{KO6FvB%Ju6nA8zJlF~Q_U+SmJvOqN z&Pp1dl|XF6UX%u~wvNfl;(b#bLjw;-yKQn5kHOgtzyXxBhi1afC0oy@XN;D*-N9*% zzFY~LTfcbG?%MqT6!|QJ-h&Nw3x@S7^VGW0FgguOqM8f)ndOUTjLk2 zbCr^0qf}xsr_gg>H^b+NfRo-j|5fzl7qH{i`SV`|9IyiJRagtpz%S3OSaA+mKnbvr z(3xAUe?}Cih=M^;N^zdZBR~A<=>CS}0x6rN-@1JHR(%#LEl4)>AN}cJxkq%Ah*KBz zcoPoIS#b`2+2e(<;8tpAsMl8``u%dOjR&9@BQb{|s~;VKwRgufI8l3|ZZGlxqLYge z8qwtDqy?pEJtzv0RRy*!#Cn28ZdEmx%a&(}nA}pvad%+P9b?b#+%)};KN zWt{D==4vbWHbbt-ISUqL?P+e_Gc)qhtT9`6y}GAk*W#_c&(gp2%a2~pE&)uRT=2Mf z!J13=-7#&`&U54LT$loKNBzdiRW+twH1S&al_9@R(YJc=Xfw{H{k8I~i+8o}d1cSm z#<@GsQayeA4ko_fdieOoC;_~Z7B;&{bddRf)qM$k8^zi8&g`Z8T4`n7vQEo~WJ|K- z+luWti5(}7bH|C}-1iANNr)lj;D!WJAmnO*aJD7Ta1|P$C6pFOxf@!V1m3ok5-60m zkZAMG%*u}Kgwnq6_x^t0msmSHv$M0av(L;t&&=~Y|1|MyL12rBHcM1iGJ#$lG`OL+ z4kDJbKYvRv&p{OL$8LGtwM8MX%SvJvN5bPOFP@mJ2)hzWgIcjz#qjGtyz2ck(z#C` znmhNQPXR+haO+^ExV^VT6F41juX0;VW~ZL)<2CuK1Ac?n7Vs2SJIwVOu7kI$jy?t& zQE~l?m7W;HN~87&pQqW$L_VxTTuV2$k?md0K`ju%2w|vid4NC@T@4})JFs>S>2pX( zqy^b0rw8!Z2criQ1SXHLAN%qlfO=S^1Bh5Ps2u#DXX@0RPH;m_qfWY&*D*A&UJnj5 z+Vt9Zxywew7uoTCMrAVdyx=jandqC=DXm^`KhGm(N?KCXnU@#f)G>cu0rs`Ff!^t% zm1;A$Qu-yWplLPpi_RgL&d$t`tUvA-t>B1;hqOX_y|hcpbuJ@(3Z>UwNVoN-AIasf7?=*A8z}FaxKP@# z61PV39-vIg`@r2@c!eWKTl}GF(mqY565$tQ=$q#4edL7X#g07oGs+KYdq*qUh;4 zJzV-crO4*=Eap)^BK&;L@||$IDeQqOMyzXc;EH(m(Gk;cJ}#@o;ueh)&3rW9g~CA@ z>JOu23Mo@M<;JE-d@6^Dht7z{{2+16M{}|^J6;7(_kJsKF7t?WM9m=W>${N1C09ey z%HlzpQB>QEb;0u1fXY`ItTWo+WxZ$Bxhv8H<4Awq@I)!CrKj#GFggMzi^UXh7z_4H zW8(%ldUOjZ25j`8#Q&pmhn_4$WM{y46tKHIPvqis0&H+jT zeK`W(QuY9wV}WWyJnU4w-%YfmLf$?-Da4!-Yzh)1JrRj^xqiwK^?$ja(s+*qaq+!& zcNlMn4u!F*8{@?tMEdP(D7fayYv$uFgbAKNn*_oIzCgmdYayoLeW&yxm&YGST03`V zUpSq8R^!v$uhDQBbokgltl_H8*R?))G)L|`a^w#_#Be+~BKMQ@jAS%iI(|mwLb9y6 zFVavK@<(EmW>ur!lf3~Ki%RurI1U}PAKQlAxuElPP5(7~Gc}2zE@21{+0S@xj|Xq@ z=U9O-X5}$U0Ez9stcC9P;k^ztKjI#hb9z!oe2M22#uFENN26zI5krW$LbJLm+1%u` zI*s5DqqG)n=Qc=}eUVq(b$iQ!oi@OTy4I3Hi_0zYc|$$^O541N9XlplIDw_rtCy6H z1~jXDa)5DO*3lS$Ij*JwoRyjMa7dRgRqC!_6>U&FJ>+A~cUnNsAZmXcs4o8m`6!lu$p=Ob>CXLBvCyV9!%F#HUikUmcQYAO>bZ4TP<9 zOfvdvSiVA9k@oxgVA9Q)fN;~$X+&&=vPu_0(M))aX2{E~f!qN8iP5^O;qZdR#=y`R z~Cl}lmm+I+Zs+rIF`ROlX%AB}qRy(R7CMIy_qR4VY{ zH$$&@c4;yNR*z)qIR__*9$`K6dY;Rpw^m92xVCugs2BjOM%4z&+d8v{crBm}%4rHA zaJ{GV(L1^hZ7=Ux(C7r#aC~?uzo35F>h3}%q`_CG7oUFNMnNgvF;n_}fUd05@;^m1 z1kn7qi9JizQXPnop)hJHUPi!DFe*7mNZ4l!_E1s++*?&ah99J1sfm70fP$|cy{G1LP{S9D%Rd0UUud_KUPoH1| zX8;ZI)Lu`E<0i-fuZg}_&*)1v>4h+|qdfD0uP_n(#HRD*x8(tq^o_+5^tYP-x?OMa z1xFd5pQCW+0S&B(ge&OjrrQcCAB@&Wv%E!2g}0(0m}0#(k#G`Z*i6Jv<3tiByJigOz~oF zBt@Ss7`B4ZkeP6ArG;TsypA)$CxK?E@p6qxwPEUPpaQS&G@Come-9<81=WU()Wlas z=zpG3YO5=0sUlpI2R5j6*D?!F7W<%={}G)m1I9-mmp*PB-X$${nkTGx7B~-IX$Boi z{&86Oqp9w&(rhqmM1_?;yYeNipvoBjOOQVOlV_yorr&2?(wdbhVGW(+^Q^3tl7`br z=H=-T&Vr(BBcm$jeh&7Om(#@>=_%FR&Sk&^EXy+wOkMaatS)e_pI~-6%~u{aGJLNd z+4mTUU4Xd!7{SZMqp7T3N(KQd$LG{>y;yQerNyur>VYqeVV=Tb*b)l6kzj=v-LP7b zJpAH;R0dXJ>^pD!!=HBS-2TPR?g?JLq3zIzr$EO^Z$o9|SNrzqT=`=+4KLBt>GX&# zla^%1ww)L*z`_?7`F-~2vg$5JOP+TH_`$pT4jkC`?#_Sg@YH3Tf4~31Pd|Nda+@|V zv-PO-+HAmjZ@mAFA9fD)?f*V}=XCXX>8aMWn}R~ut+rHkaGbr^Z5Us*;I<{TZHs#S zW0ASTPDQ9Fnoq|O4<1B)jLW$Tz&IHMCE1&z3E&kkR)drg&lX{kO%ja*0& zN)IPvdExaS?3oG@g&!Oc-6}G54&3fNFE-9~@!?oFXx0>{83k($Y#o1Wq>*J*ngW%@ zkFM~Ut>U#%p*Ls}I)A2kSfprpQO2)JXbn0AycU4Lt6|rOtbS5P;Pj%#B?>kJoGy&^ zkD7R|f3z?i>hsJNmqyfc!gVfIjEZcbpmh7)=ucrTU`23t@H!Zv^r#(HpmxBmkdkr0 zWJM-|J4hUGS#$7UP}Xb8*)z$_BsZH(>R5vU%8n)y@f>(L-M;nhN{3RXGc}l8sruG> zO>pyQXVUpTuP|H9+qP}nwkDp~wrx8T+sP9@v8|nV zYv1>++O68%`{DGdb8mm?TXpa0?thK(sW3*xydMYL%wnEf8l88wnXm4nLs1$VF1F5C=m< z^0OsOTsTCI{6`A{st_D%kTm&^5=GJIW^Y9UkVbiu{i@sYG83~Ws2;<>qZe*P#G8E- znL~<9SX5X;dKeQTtz6N(br))Mh6VdCMgMcO#W zmlgCpAM%=GCZR~HrO(EF7dpp1UIy|O*d`jiF?{_kL z1iLIm-L>4YyV1XBb&_g~0#eCdAnMD8i*VTrp|`PkKI|1gfG%-7F4~ly&yMp6J@*j^ zgf%n|udr@K609@35ia==-(d&*d}L_dE}ZIJ4*uIfC2j>*fw}99)|254Hj4T&b3Rv# z0$21kaI*T-bA#ZnQ`R-QX|8A3&U@YXWKfAy0>@^B*~B#zv2wIgjsurBM#+4jTPdC_ z2>zH!lg84RpfJejhbqpwUihLt$mrnM#k!Zwb9I)v9bL!X8q?eJcfyu>K&S8F+K3wz z&9wRHP<(CyMfQ7L{*N7ws%>_QU${8E9;Y1_51SC~FOwW|5AY0mFUQdvx0B*=RFe@5 z8`tuwWr;T)>lFQ%7KD;nSlchSy0N`u<@yHKTzdR0DGDiyDVD6d(lsUa1z(;68z8@> z3bLPtSQquUnQ!nMxj5FXSXI-#d;V&v^wf&W8PO&0s}Oh?TMy`5Ow!K#9=gNsf>B1mqqc`#*k+b^Ux~g)Sd(nm z$5~c5?)IWe*|rJdwI;g^4V#6z`I*J)kXp@d*1Ee)XS0j_>tP_1(oAz4)XHck^{Fg{ zie54eQLKMM6jii_f()4k++#RJ8v)%kOA4IUmLeUDx@D=_6YtP)UE4eUGU}LmBMu!& zT7r>6(6m8f?%+oSHAYpGAB%lSSNV9)f}ZZhSDM95%IDZIpR4m_F|>g1^ZSC13-!Ta z-q;F6=$JOw-XwGt$9C(v$8^b!qwfRI)A+&i)b!aeI;-lLE~8HoK%MCBvKUR1CY8r( z`m{Fiw=l*xz{E<02Z?w4-{XIyUQC*D)}wPoQ$Go1EL*$TMoB6D5=ANd~KUtR;v!IxSJN+jziV| zmS!+_d%q7SKA*o(Wc3?OsotPuLo|Q3lkd7rk56#)xw<@NuWR=0$Fj*tjV_0DfbnvG zyBwIM=Pwyqi-q7hJm3~_Q3PQPi0d=`%7TrQ<*K}ZdX7op#|xOXc|VtU!aK#*`rgWE zGC$RqZIx3tuxO3II@?ky=`?k#cmQ)xwDVH2P*AW~bkDdjC6o@PHM(I8eC5 z8I&o#Ev{7R3FC&q{x{q#q1_uPteoE)z%kk|3)1)+%QR81$CeQ#vJyHUzr9c(yH*S; zXHLZdSwyZ2FY-5u!p3V)G=fi)m>%RoZb#D%+YQ&%(PgdS4gXT#p({qULZMb`r%^z-PN@ZHb(2E7iv4!K0)6>CNc(zsDhH6!AvTZT6rmJPP_DWbA z<{-5uZf0^$XDPj8qJcJ-r1G=wU7Mmj%QoY9+Cm zchaL}2pl7Ue5Miam&AHWELLunG}Nr4fjwI+!$>&!F36<1!w`^^vBS#M7O*wtpkhb~ zEvWUsQ{$fY?5Z6jlTxrWIZ*40yeg~qvSdZlw3RHZ?DYe#mEFCqeAIk=soNfQ9;c^M zxx={MY5G0Nt;8gaG`^j$24K&1CQYUVIAFsI4tYsRF@FEPdGmIC~zQRn?X4RF=L} zl@4f-N7CE;^LI?Jm*dDB6YfEailXZa(=H}RB7Oo(tBBQu5Q|j`4MiDnWA=4TtMFR} zMt*{0eRU)3hU&l-s(TSv=c|cD)S3>473l@#AB`e`g_X_5Y#im(eBKSc#gnwTp&~ zlF!RU3z|d$#`ZKws~>EdQ0&?#A_%mdDaM355}(EG)PU;IQD=d;9m%u2vb%`y+?bO5_m`8 zIV$y4{W($SWX(qM%LY!3X6gqGKBN#%7!zxm^O`try(?0&7mbvBgjZq2pOqoTcsVT- z&7z#6kAgeLNQ7mu3sVjL(hw&a8f|c6pk0G8A+D9}WR#wrp%BJ4oVNaL50q?waq3Ru zjIZV!x-p53+rR10fh#AXu=$cFzYbzK`KgI{?H3}W4@@;m@x+7P@!|~z!W~E_Aq(sf z+EkvGKl!ZWHH+dca#Faj9VQk6x}J_9hib5d7S58hx&31bZCBjU==_BZ-a9(jqxo?e zp63aJgUoMKgC5w{Uik1&YM(d!xravA`p>3$!Mft4X}qm>=9kA`7KHEje0f9Y41r|` zxjx4SSs1bwYiue4z*ovXTXY$Lp+*zL`iDGXa0ABvah3sSy!4qSvL zi4oE93d9LC*i5>_a_+(tc$zzf@x10>&N0em3BhB#c6tT=^LWnn*6%L>WKwNc)t+rQ zkvX0nkc1p}+fPDKlgnqO9))~2p-lM*`z|BV$i-YEE}aSNO5b-3KN@q}DT4K_e8v@J zcLrrGHc51`i^5~-k|M!FRatDw)EcxQZ_+9#A36He4}Vxf4U7Y~&V>G!-fxDO-rHqT z49hO&!@6W1nW-*_a65r-gHijG7F%WJ&PnDs4N6qIG_BK1dj2Ij$ls2GK=nD86DlE} z)ch#Ma*jpZxhi_$I$FNdDtsm{(_*Kc?$L#rFgvNyqE_m8fvOEKtffn6<|f~ZUFvqm z)b^(V^&w#d3JKzS(pSqET;bRPbt9iW%8Mcp$(^51!Dc4_W$#ZX+`eD*3W!IIiy+2l zD?Td@N0H288#Eot5>7@&Mh!*DRkrcz+R6#ivDOeX$ z)r)yslFRGsKoOETT0CzL#$Jp0YU$Am4w@A6o}`NGmU0W;>aj3~KVNevfj`oz9VcEu zmN1ni_8b=S$d9fU$xOiXxBPV?NrQfa>+JujpvU(BTkFc>9Ve7{^%xEVZFYmkgiY&j zF)B|@7A?`Hw_iK|4j~sqdvFsUeY?8O0~PTv$~ZcgHMsBHX89__fSgS@o_2p`JIv@^ z`K)BP)XgRa|6S1?fC@WRh3PH4+TVd?V~LjU6~amUI6>4ADv_EatsJgD8`DD_XAqUO z%F6$^p%QDu9t|r5+m6z#o3+RuUS|I$>;3Wj7Z@63K<~Sn$mCiBUATtF_1hleo)I?u z2b!c*o0P!UInl@<>?5-xXl44EbtHN8Yj7r+J6whffhCiU9Q1rvT!eE6qqxD&WC{NmYTtXg0En8yr=}tO&trS7RpmF} zm4iOSkheF&p*0^;{Kzkz%|K8Q{Z5Ub0pn818f8dO2Z(;g6L=R>%s*bN?Ecy!x04*X zJ~yLj(YU3t@v#Ih+f8G6|K>o6oThpgg;KcB7u{-|Z!0-I?DD~R=h7DTUM}}~*L?x2 z#~f`_w99r|T!csB9MikdVOx{FE@#Ibd7vzPR;Uc0M@=0Z&#zhLW&yD5f8!s$-yg}D z`15IuLN;VTcpeL^5P&cy)Em1tby%qDy_X$!o4H_6GX?W0sU5{Gp(~6Tgd-2JlHS6z zq0oHM78NAiE$jba(d6!?1zqlIe{F6@c)m?u52=}_ihpo4lLROP&QO;Sy^|q?rb-fC3u?Hum6}s)Tmt{n3h{6Sd{7)xQHHS!S%gy8ZU&)D*t)a|wNOZ$`f=!i|Ni>o z!3?37a%L9klEJSXt3OyDo8)`&^$AeAA6X_>bdmEw?6{i}Yo5Di2$~{3=t~y}yxZp4 zxoj2h!xhm=u&n(4v;?VJRf(n+^c1LimCvDbfEe!M*<4ZLuIQS(aD_^ClPjaT0y2u{p+(<*hh?%h%(_ zK#dOnhyax5Z8}}xp2j=G*;58Nz;x)LbTgGUW>?McY-p>E25LQQBjC%U> zM%^=QTm=pXCbK=zY1vHA*;G3|)tJCu9-V8Dr{89Jn`!D*yp+F`t|$BthDSB>Rs2s+ zZPgOX!V$mKC-+a(zw>0(LJ;D=ruj%HIB|Rsy+T_+hf_6Qjdn-4M(g+BX!QLU&dYob zTY(fG%8A@n(HO;B4(^NR6WB5S^L;1hZ~gO@f7(dGGtW<2Ykj(DLA1sfQ%L&WP`<%{ z0Yc0O)&&#mvRFbG95)zsGQIadoZmYjTYgj_KWb;&l2R{7DSjeQr!0QTl*B?8;c7BP z720x2N={`-XZ_B*VPy(!#u6j8@Cpe)il?1c<5QdFlVbxmm!4whdzVV6-<=bm@JUPv z*na4&(xb8K}*;B3G0 z%6Yo^-@om)2Obx`rMD+hQ@DkCi#iSk>NwusJ*@e>N22Dx zonqnruw*?;pna+wO2w5>%jvD@TavZq^rY-c>HB6k+N8O+$ApOAu5)oZd-O*-2pwt^oc0$s$ehCgF^23VTTP8AltR8*&y@ zX{3Sf@nyAAuLnCzB98C!h)-v0ObGJrxV|e`eXmX}?F@SmP`Pkq)tk}a4{#7otu~VQ+i4YY*KcJ@` zf=7@mnTkFSK1|$ss=)5_=PlK_x8`Huw8yDd!aYt?fK&#)0<(F|iDfE1n>?v01h44d z2Wq#&*Oc4T9$$*Q3xl2jJBJW?`AoP)+xs`TvEV5j`ClET-h+hXJDtW*g>m$_rKTtyg+W9LQRHvN%fB< zwg}ZRZ_z`aN8%2ugfmIWXlrk?}X-m{v@I0SmU z?iT@oLMxczO-(N~wV}#1bz81VH8upLTQ6Ex%2I~l2R1@ozexcHh$M1aACKc?DwbV6 z?puFBKYF`#L7U_f@;ZH~c+gu4LMXE5s+W=Y52u5qh4Uh-5;6tsMM^f=?L6NdpqBO*+v+=?4;;Qq< zO5d?>(xm&yk4(g$neRl&W~{Q=V!I+cu?a`!Z~|M~2Ku1RTp*it${|M_{{1}^6aP|l zqsXiKYe5wp))f_G!x%wU?|-rYF0@+M<qQ{w`ezR;XuXcRGlEj- zJrJhYv9mija`6^MNF&d{{o`tFl^$KT>>nNyfjEyKRK%14g@VrweM}>od3JkU`wdw154l}2Th+A32y-zT&N$i4k5(th4d*~>pKcBZ#rz!x)e$@xayog3zro17Sh z4_m2sCTc}db1WZ}+>C^~bgj^j@#$yP3Z~^!XR%ObVf`HpgoE0R&nHeFd-44E0C)B< zjVM_AP8$n)6f>P&1`?WA(BeGpbf2V74}Y!Uf?|PUQ4lD?oU0NcUpT*pv2jcr5rgVW7ji>ZjPw{= z09}|c@xBHM&xf|1h__r<;lbOq+6kp6z!Rh zak@|q(|V<7k>YuHHcGvBDwHp&CV!jj&QYy!+`+-0x3f`5kH5Jm@?lXu)|*E87xMO% z>FoZr@B^JP8~GuGhZte780f!AgQHB6E|7KC&ecmY$HJ=?OPON5Sa@+OxDNJpI!mhe8s!VE8o>vVW zDLkZzK&(EdtJ0jn5oAfUS{utL;JK0sQ9pnt@r9g)paR(*m;RNw3oHo>scyh;qdi&Ueddl z6GS9FX$2Zt9Q#Ft!&^9nF`~z6N&}1Y7ll7eF@OLJAM;m#1#b5V5wHn!P~I~ zp&O_>{Rt=6$rYknGe4aEnVE3~wisT{wlYUs4@%kAf}h6UL2F>AF>eSn7yL2`k>lP~ z%H?`FodpY9Am%XZ!pTal5IgAe9$SakZJWAS=1>70+bL@;zRTdLKh!h!728;-pHM)K z60cIB$O#o2j?VvrHYY?L*fGV;J-r?TNu-{{A;NM?EXr;Qf(tPM`~g)%tT~3{>%}b= z)?h%!QB*V!WnrT?M6PO=WwHSLR98s(rD%XQ#bUEeT~G4*VNlFa?7$!3O91;&iIkN7 z4S@yKIgtF1iZ#i!8Q}au@sDxy#CzfiWoQ1VQ6D%sT)gYUK2RL1}Qe!8lCUuDg@ z(Dkhz*?kX6*3Sk=%0&W8qjfiitY7# zS|aE%cYJtU`_jp(igde#%Q0SLQgHV6Kgo4@x4)PiBZc>|)gs{YO~G9@{A!&?KkZR!982U0^cF{&Z~jzY+)mifl<-j` z3We66@JaEvr^H1E^Q}NE;&IrVrn;#A(Hev$iT;;B456MqC0l;q(JnHxKqV!o2im)A z2@3>zB-7iKj^xjBf{+1#SYN=i?KcPZ2Ns6FMfH!ee44xf3CeS%(YX(HNWUx{#yYCa zz0rDBbeKho@BIyFSo(sxqv}@??{kUsl5f^7tzPz_U z?(cqu9~GEdb`U4#LBWre^vx_IMB6MX=p1m@ti1h`5b0?Fe^C8^dxa@-eZlGi!!%Wh z>TnMHLOBBY%y-6fA3afIUZ4SAWIm!+-54175ZeevSF_&xQWQo9AMubGn@NY^3m#m$ zM_7UIEgLIF;teZh$-lEdt;wfG-snS0F_*K%JaU=W48o|g5E37Fl zexM%cm+P?W*e@%rt&(-egFq1_9CjEq)o>TL6j#~txmn$UL`Zl#-5UR z*Z~btbX}lpktV87Kn2416yyrcm7^=zmeiI+mQerEZL5}imL!(2AL7;^%Me1%B#m%% z_Vc}PqOqDUu3@tHTtq{Ol!MihHOQ1rnFetv?)h@vlw&9v43&Ix8ndQrASFZYsLvQa=k&x5{9vkjk<6^pWHP87tNU<<#jYv znbf(9aSU~ix?wq%gfg$xG5)z_n3hZzD7^msX3Hfi57UBWBt(qgCYjsFr~$B(UaklT zGvK;~>r*jyCsP=hU>vuZo*4}lZ2tB?E#}T`S?wGLf8*?6&X>;<+dwZBNo|=5OQa&R zqKgRQM7WHziA-WDXc_lfJJdiHfY^0~_ymDBepGuYnQZ$AU;_cmAMqMRnoqn|IN za~5cmttM`bMh{(>n++McGkmb4wQi_r&0YN68-%W1mvG?TRPjH;nShV&IOWU&^E6^i zN9yQlA(pw=hwCN^d^ovaLCC^_V3`F4scH>)@R}j$Krd1guI5t9g8NbUw!nfWY|Giz zU^SSQxYY<*gGv!08%d{c{u0CEmC zqok%mO-#iVmW;4C=~~2oe2uyG*T##|jMb)Jk@DM7S%|93wgz14Twi~sZ8ioGGkWbp z3yORQbnWRE3);vfRE5%n84FjZFsWX_(j~acSh&Lb9Um+ zT(o7eA1e2gH68;%RAKj8K|nw}vrP<54Gj&Ac=`5x#Y}norZph#-64_MjeS>sihqB9 z=LIGGfge6HG&BY|0|7Dp1-ts6eN0|v`}_MRZU}#JVq*uAj0alLfcU^b%>26_t1e@M zCWKV$^}rjGMH`OJ2Cgn8n@k&34ir1CC+LYJfQuyA7b6L#aIyZt{z4om>XYuSQDaf# z+igy&mf^4L>g?QEPMTV@*f)4fqu{ah)-Rb*R5{YA;H^=x4L}?7bWTJM#gafp<|CtL8URQHJHfb(q8bfIkzRjPi8E zbMR8VCO%i53l-dWqL7W)!85X@iGZepxh#AXr{ft}G->vWSuNRN5^Sw(N`&AoGqn9r zW?ij-z1>BhXKWad5}>P%oBA zee$ustjIrTy}3#J#9{C~Y)5W=Y{|Lsq2}=SZQL~v=p;qh+u$8)mV&;8?DObZjaP?d zlSB6~;@#)mi!BFgbrwVU_U8reVvKW{6N?`>pSwu^2S(U{NFC~>B%(N9H}Y74d)g)3 zZJyx0)xE9r9{sy>F>AL-$z3zT{X(7kOKIbUt*QE8b(Ac`mrjq_)4BW?`0gpA#!?^R zkwYi?Y|@*RgA1-ktcN#ujrZ5qnNnSaRw&rL)@L3|>%ge;r`OcE3{eEXz}`L0uWR9$ zs+ecrFX_+T8gJ`TsFpW^kRx`87d^oqHBq`g#R&IletSSyj9WiXNXv@G^Ckpvi9n&I z4$vcKCa%>x*Oa_^sk>$?m=jV1}dKxp*&ViPG*)QjrQ0uzjuF1Jv zXGJC_;B;)tT=x;mtF7=;xK9G%(raUopur&}_j*-Cr>VT}>l7Yvy|L{Je$yw0GAkws z({puNd#LNzjcUrfjpn^`&F~20d+V89lIo*6Yk@bmJ9{8c-w}?4V>K=O$21DbnD_uG zx`U<3DoZZ>w^kZ?h1vH@zsRmWeMk51_3XW$ z{6b#f#CIbAjt z6P>vW21pQAs1%~f%33&g=J&z!b^+caq?CVV3j*9fQAU+`x8@}IG0l)>+R6Fti~k1A0lx}g3RIM5(;_7glACnP7_}~@6adqq0^mZA6_}&IxmpA;=6qmVEhr4nnmS-`F-5tm1q#+j|T$?PMrAf4f?AwxMiXNosq8}vUMXb zO`+a0>pD>$lj&N#?|pz-XI2J@AsF-4AGtIctJG(tjw|X1J|rzDx6bg_HqON@584r< zZc|Lq_EOpBkDkrB*Ct?F95?v3fxF_~cBU9v>67Lk8?xJUOB=z2I$RMtdpWW@?E7s4 zRz7b!7l9HmnI44>nA{#J4u~vU5rpqI)&d{OrzugpP&YRq+=%-DI2Ppa{1HI6NbZOV z7w~^1K$(ciykWeO6D3!?kO0V*xT0^)d!C>bR9=OJ1JZMfd0!X>`KADzz8Szf_T3C~ znXIct;U1pN3BZlOVRmTmN3U+a1V(og!1vEuG_X4~b@D>*III1~NmaGMP};d=`%K4p z_yPRB1M`8-@OGgG!g<>(#&uv95$5idQ|kA=?2g4XXfLnm;xA{ydwjlu2#OnDX@CBm z6P0spi+!#h{kf(v3&y2fMW^`Xc_EpyySuzem+avva!P373*kzO% zl_qADVt-W;Q=It8RE7v|s-@)V&Q^_Q!@4(ySBYEcx6a~{oy=xa2p%K;wjYhRLrr=r z77@>iBZKV3){V2?f=e;$Lo@GGbC8v0RKa-^SP_sOL=)`tW?($rhr}C{%F=MY@l1lx zHMwQV;v%(cmeSo`3ck-X3-R*wmleSZnow{;6?L)nx(bQ>1kkf=1LpV?$&=d&9N#JN zkT#PDdb&ZFdgd2!uipR;g!@BtTbKl&Yq0T2rwVmnRLo$2S7@2RsvD@tE+Kwr2f|e81 zE+oC^^0xGLvMDEMoV3PPxY<;up%>MRqbW0p9*sgXbiaTc%6nWs6u>0DDT?#%zDM^< zh)WBOgN6$R%B>l^?#f*+M$b90FYcN2Lvr5_mcU-jgn7qtHvRI#VQd#aI|3gl6Qly; z=ds|hid)~BrR{SQz<~EW=pexLp5a05jgbFJ^ock~2EP;0Z}f&|#DG67vF97}hW)@h zW2^9wR74!uvp97M*E8dsI;kB;w{2;6uscO&$Bo==Vl=lyuYwL=8lCv-==e5ZFR zy!huiUgZs5Qt=-RU1QtKdIbboKn$bhhxrV3AJTRgj%B^?yMef*`D&QH_A62X}V0M)&MAU{=7&Be%INeD`-&=u28+3{x3agKlm6|5oa`0x?IBu!8}8&wv||)m$zgk@UH3RJ<@01ORv*&UQkbKZ zZfy{tOt4F&Jx3=#pY~UA&gvR}OT30%#Xtzm^tUHcX(ijzM!xP7WCy{w+cyKNn2&qT zcNFx8dVwhWAp8I`>&bKdul$mGigY4>2IPmV;MC7hI5-4DelQSxN>I6fxnfGvt~II< z+GyW)v7Ak@;kwz^R<2@y`;CGj<-SRPrt(_rwGn1Hl`JVH!fg zZp`inHE_ZK2MQC^24OkLV-AbskJp)Xi26(3u#nfWG2BUnzb~fiV$i#^n2v}7beKx+ z1lsxor7CUR((g;o&WoEq=slB!NlQ#ikGxR3$aC@ytiRrm4@;Gf`0*F6 z2Rn6_6BSmEXX&E2NVFqL?KGOhnypc<6EAf|rP`0X;wmy!tPo7orDiHVlDfB8)wZs14g`Y`>YFE8D+t!j+#PKjUg{YS{_IVdIx7*Li&5~fuqR0}m zzAGQmTp66he@C8Tn*nY3D&PF|^*Q6OM^3**Z@4PFG*A}3z6qH=LB+^39&TZ0qt}o< zv;8z6To1+@-PAISDX=w5+oqD&QnP6l3^Ou%8n;{7Qt4ue7$>LxUGW)DOnrV+Q}yu~ zmBml8#~&{K@(ZNfz1w~c8dOxWpM3%^IG728XeIX2dU>7nZYF1`OEnd^%55d~kl?|r zrbMt@<3mVj`9Fske-zcjr4GSpLgNmM)xpM!UhllAr@tXx~~U`uE&^(fCUJ*|D+F>0Vub_ z(MQk#q}yR?!)*ZC?Fh9IxB&5XX!~#-fOaQlMw zLhlAU40!;$ZunmKKS2C{3Ir1lDFDiDSYEh3e)vQ81se=G0NQRKKM?#80|EsG^8m9q zm@hOR@LveufdPYkfZZFy7lu+Kq(6+Y*i*&`_Z9e#KVdb8jqnDPbi*f|AZmwW9Zj~t zIYy=(UABI-4c9o@Y(egZZtlCc^IZkaTm^US+qd&v1^Mjjw{u*DyzgVhnLtl! z3W3R0?}N+l`?m`a1VZf#c`_0NS2@CzIYC<7D)Pc1j{Ulkb9hyV;bA#OM^}k_s)b)6cL5H!@E`bJ1pi*tu)tp4EyIh(2ksaCchL86z+T_2z>9%2G7^eXCUbHL-jP)# zjB2qFPJxp4zZG|gn&MbXlZ{aJl4(nqjo{Ye8cUmv@Ey_31@~sYOF^Cm`DT_&;jRVy zW}ZtSp9TG9j!TjE1*}+=-+xt!Lu4x#z~vVFn+5O%p%#Q(8S#ayETc-T!p%<=xnmH@ zegP%9qvA?UfSTNKab>7LQSRUJr7A#G?pXOU7N9J5^h~J>P`7g4%Ty@`XNgpd&RQkH z_Marcxm?1}d7_BzP(_efj8)>kSunaeb*2m!DBKxIUn&Ds?u?-?qX9~HM%9+u0JS^g zYRhne;+?4oAQcgO!-c<^e;jOAp@-*WH(wHowq-r4&E}|dwA5}^t$+IJb}32PSEayTxbHfb z@3pcNI6&mMj$Kyp&X!uIqLzwul`Ztzutj8D`R?w8!<|6o*d9uyG`zcc6acwajBAYE z;U$>L%BmSps#5EM<@Hlh6oBoq_MJzXmp>dzPu;e9VPITpQ6E)fS5=neh_Mzf|DBY) z#kE&CI#btGv20oVz$`wm-JF)0Z~Cwwy}$HNx6|Z1(m74tM11X7oZ2WjT8lL<#~9R> zSih9ljNH6;XSqOo(dsgAQKi9?&xBt_Ofit%fO6p*q$JkM887nJ=fm-`sDDg`61e8k{}G z`>9v^#``})6gz_nC!#`fF-pL7zinD_@~BO&Hr&-;HY6hwgPf=E>z}Dv{lVdNssh0F zy~uE~+JE(Y7O0nMzVfYJdwB@!iqcsR)DDx}4^K}Te(nE4A-r||;ZsxDLNbQEa+zmm924D!y}qE`j0(cw%8g>VjGXG;^1eHX19qvnK|DWGdK8c;mYF~m^km2)N0G# z+acU}PYg(|{q}wgT&0F;lYKVrSRjl7lNxi@9^vdHWg?@vcaFqzy6{h%&cHL9i4I0^ zunBdDzvHr9I&{JlzVJ_-=$SEYuwxP7yA?vg4<$dSM|^QS>cupPrVuR(napy9y@iF& z*m3l)U$td+VLy|BqiP&^Sr`Z9m_Yn-#`>yUkNa}-cG~HjZ7dSkG6IELDI8(8bQPDi z->SP6)om(@U@EphzTquVyJbk4Yq$<6@~4ehvUCsYYDLX`=Y(f>B2;}2z7bE!i$%n3 zSG^`2y*!wcqk|%&^;%qCdxm+4;CJSFXCtSu;x8C2>3D^aJLB&)eeU{WRiT+Ob&DeR zb*I`{|G{yg)xF5QO+9pX&p~$!%Ki4k`{t-sMGw{RX&VmCDT&xCq{;E~y>p(jCZx9f;keo|<~ zil$7BWv7x}^->yY{Ab&MC zA-*>H_b7*h`X`Tzw!zGC_{SwFmVX8BH?Qx_6Fpe6KXXQc5g>dSC)2|FIpOG_Llzjy zAr$P53h7~iWY=cF1Pr8$`&G+jxo3wPc;~!T87GXG?<5SnD0jz}TahBLT^$)GEXNmS zTvo5fSW%e6bzGAxBRu$loav+!B)xs7kP;2VL6V&p()C6fr8XsJrcP4kRFKHKlD)mH zW36##Qqcxkl!!j_8!gW6t=5$C`OF1)2f#OTy04qFwZB$z2qO;t&twuT~;5c*ENEE=ZfA)zq*8CZ8#0$}| zor^Y6snM;KG=gJrW{*Ad{?(bJZ6$y=Y{*8|KT-!_@pPpp&x8KY|ZxgYgGfzq(Ts9l~Usv*3=Q|~qX4|Ok4XkqnWEbrn~>>AO|v9ZsgUe*QZ5OCj3PM> z-8;ci^6--vmFzz01Gd}o;Wf#`_5Gks8WA$8zsiy7sNra(XlhjC#pzRGe(!U)Y9_ub zE1dDNFqVz9dZ2PJmdb)jKQhtg4oy4Nv7?dQtWt_8Wt61MvvAVlsKnHwpsB!F`N_k0 z@iFJx14n6;v6O!r>mnTlW3Ad`5iGU7pG)U0YM`u37CmX*QjNW-B- z!1H4e7ZZ^~5SNzA!WcIu+NT&}ucK{65&jgGHL9m-$4VtL|5vc?zk|>Q;#x>%Ldg)s1dM-!%YPPQiF<5k9X{l5jPOl+jaRu*E8bLP8QGBqUD665Mi zu%~&7yewF+|5wyQ{C>uAM{Am=%FBZ7y81Y0xw|RTL;ZdxN`;*5w3<9;xwt9QRXu6O SdSQM28?+M|D(2r_;{O0|uQ74} literal 83588 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�>=olG4!T4BYNCc4f{&*Kgc`m= zjJH3zZE>o>GG*ti+od(?8;3_~`^-y6Vc-7QLko9Ku^3YMaD_M01hvF8H1d51bH-Qg z&rU;(FDDJ`vnPQcsLkm2u>^3J_mzRA+|DUF$&Oh_oso^^4x1UwgG}Y9+56ML6H|6_ zrJeYDbf4FaXVJmTWa49(C+jOw%o22n>f)U!(|lA_N#G?c;Xg$PBeEGoyNtk7d|nb& z23S`NlA1R~aYuq=Ym%jRMLT~X>RX}|_^4SV5%lm}!HJ{gma-}ywawnYfA+$h!DIK& z_U6JRcmVI8I|@BA?$$#`ZRr(Ws}a-{l!Finp^uaG*;;LRp-~SSZRuY{mL-_|)wwRH zj?fh;w0!MdFt@G>)Mp3q67-#I>7?CDiu!xq=&kV10zYqC+TShTe}2C%d1LMD{kp&J zpB%V zqHt{!`YiHsZPSCx!M$0Lx~Y4leVb|}&Eq4ldP41et!`GUllxNHPEJYmPdnS8;|$)12a(P+_;ag~KXu(rZ+JCF0=`-mWjRgwiZr zJ%f{tVMLj&xI(TTW3vp9@hJkAu+F$3ehfIM5tFE_&RJNu=q;C!u_O=$j3Y+5gqD#3 z%;x8pv-_<}WTl?G5XTM*p)+aOMb*WiEiDT^?I@B?m2$!owj$46CYxg2#D?w1eAhOOnCIO0~Db=Rz}~Q?1Uc$ahEX zgAcj$uZDeKjk~FA)?S6erp*>0<4hQTvwZa88N<;CFmCvaxg+J<#@mvR)0~Sb;DYLj zu{~eXwmynPGKC^?U7y;OicY|t{?CFb8J(8+D*AP7RD~%Oh*+xx(AO-a zNi-CUkaj>znwpIwSc=m}ksD971{2Yb55}etv0H;^i?bB{#Urk*19Wkv0c82bOZ(*Q zw2U-uZ-`8tz5nmJ{j+N~ChIV?f^buL8 zt*`XT^z8E2F=eU_omN+5_^Y|(oc@ZDhMl@WS^nYDoB`*~c?Xnv(ZvGC+G;<$(o%l( z#DsC^kx_H^V}1!P$@R|OKX!1=LPCsD8($Snw;erHUI6NhR&T{ttu|Yqj6NpebQ@^Y z8w<<940A$cS^ovzuc3v|-bfg#UAfh*e+y)T`l`CI=q$ccl_0zcre0~ z9K$419@2Du&7DFsZ!ilq)}tW!UTOG5o$q+ueuD@cYM`6a1`!+P27?N{uUBnV8PK0p z2ECe1R2ew+5FnGQwkY+$`;yeO!YF$&7wDB+RdU%-e7?mfyDXQpD()khai>bpwk%cY z+t1U7bM&gEYP~_;UanHebxK%+MjH&^;m8w8om{SFPbqbV$d!a7!6}1MgD$4SAg-%r zOcv8iv_tTSZX5!ZXk9$4vpeLCs7xl9VULXxCT^32Y?^TTK8_Kq@2MD6bVe$Z8tyPk zUpb^#i+a%RaM{SoBda92=sf!VwWUj616q&+v^4a=b#@}yz6}mN`pe);XuD3W)q#2F z9+D4%!%0ZJbNaYF>2fx(^fmN-kNi+|kBo3AzTUZ*)=+O$pfDK7BSwCBlHxmIj#7!c zq;kw55_d3UxH$g3WY;uJk3A$f{=iJNB3Gil{l$9SE~DsqB z04j$X4V*SL(Rgat?vo^yn*FU*T)}mk9hk234aqnPb+ic0yq)TlFfz^}cw`_bV?9BO z3<&r?Y$1d$(g=?{&^QR$LCY!h2|NOiL>lomXhHTrgK?2fU7Y zp??4ijs+IdP{Q zPsimnFv(FbnEjc0+(X0Ny#mB`R{5xUS%5nErM^;VDnj+sqamNDX0HYmhz$^ku0k6$ z6_vr5Ca=Mvrt0ZLB1lv^@ba9(7ehZ)n{iO*+U{9+WFh|J z)-S9bwrJsz_Wnl~FFAVemq)n`N0%%FeHfwl&?&U^{DHBf z_nn2q(GO??aj5{-a$sFfnQZ<+bmh?IuWIw^6eI1mUvEByt{j{%E6V}%_JKP;YXLXI zB=Pk@NZuZpK;F4<#vidUOgx~42V0Hk+5e>;w!y08iNndu!2caYLW1JM_zppE!o zGIlBF02?44!v#xJ`5mu7qsrw$qIKkDMLi@NhiNHMEV2Q%588%)26C_h$kG01H*S1( zBgj#4s*GIE;?q??VY4YyN2T^VjebfUg@dU;G3f0@p4<$aM4p}>a`48AK}{uN?{m2w zq5O2X6v$tA5$E*ti!Xpf8^@2^xN+S0$o)yV>6wniD$^jEi^^uJJdJ*|;lL=8pQ$R* zk(r*GWVEUER!;4-nth3hR=wI5ha)C10j7*Rdbs zM6MlEjZRinlG1m~wlC1B#~w$gzT7r01W)BT!CqJY0=)iz3BBa>7W7XC`y70DY~RS$ z$5Hc_Tep4*LXSfsG_LKUg8(fS^mm}QK)}(zz?X*BIHEf0cVHSYgY-Eb5K|ks-^~zA z=pwh@VNyejnXwQhb%=YLpErpbTvWX-P~|312uh<@q| z{B`KqDAU*_IRFmy3@_4J!Hv18AD9jAr?`(o(5IitG1or7JfU(T2wm7?C|l&Xp!z}p zFYwt{y6*j^U-!7&3qQxNIc3;;OA8s92hJc@Tq3t@6EXYcl1Q%k|2ED!kH-yar& zJm~cf_3oR|Tnb%as7)P}FQpuY%-LX$YSmB-n)bB9D5&Bb;s?T^CSS zZbTQ6I>#u-15?!Uh@Y{$%?9jKjzp_Ftq^BBLAG?u_K7SyDy-PFV*n5dGjHt*Qn%aO?TP*;VdgPv!KhysZui7>Q6UmSS5Mwpnhi`0cNdIVo)lZ4%%=! zzCtyxSf&b9f4o5nNflIs{z6~AF1|o;{V(xRAUe02nKXiq1IX^0%Z)!*AYBH`T#$9+ znMCU{>mjBXE)am8bb?QU2@8AU_ka9qMZ|gp{pdsAgu(pCM_~9MU_SWed34_!lHd1Y zr=Gp_OY|ey*Pxh&id#>dXgwie;HoX(d1UwR1`pi*2$~z2b?e>~RO+}2)?EgQ;C^%x zeH5wRtcD9Vg7RgO6XYQ>t{~}O{F`(~!OnQx&sQ=tv`T=*$P$(9uvc6ps*eE1c`$kGNibUQr zrdYFGZ}Qb<`X*GlN=syT`DD(t_IdEe!6%OFo0F6+?Uy@xYLe!*n*b&$92|MiMf7zC z19Goy1S3FykUbv#Ma}AlFM_#$=p)69AK-^O1xvpCrGD8%ijxGH&jYz$=}^$Cr0m4u ziqh=Vs_Fi*Z$IjG*AILMj9D-Q%|WlN>tHS)pV9-zKZsiZj~tGwxDxeGJU<1g;fI?L zJOV%48-F)ogcM34p!XL+5A_zP=Pw+4;s?x+&Cb4%-Y#He%k{ z3lDyN-?zECac=+1>g6L<2-~wECXVY}eo}oW}bDA-zuF_pgiMXQ~ zu>-mp(M~rbKx4#CHZ3KgiV48Z(uD0~Pm?{mI|f*u==FK%cAMy)8jmWM`{vgM6sLRDY=YjvL7%N;BKkhRvXc7> z6ya0;6X&%k8yd5Q1XtJCvr5yPCb`}?vQO@i5}#8C&R6o`(8%Ito9Em$@dktJyRi?? ztjy;V$b+q4ItzM}l~TU>2^mUI|7`S1e}UNTG_;XeNFq&Bx-?MJ=vG zRdyVi3S#ibqAw<06unKn#A`^>TG7vFB$jVcoETCD)@+vz~WHCnj);L&4u z>|L<1EKA81`FNmDE}T9&ylL01o5PbNa*fiIj!YHhrevK)E-S0xUU*4{nm-re0RkgU<-)t*Z^bJw7OGv^Ep z&EM4c#Fna^JKi|G!=C`9a);TJYSOXuY_|3bAY+Yp-l~=F*ACD1rpgowt4b!!o)+G_ zd3}-|lRnjk2k*V(CWT~CX(&|Q)US37)G6F4U|YE@QY zY*ulBJTFnDnG0msb%S?GX-sG_67_xyxEtT;SmIXjE zyU@D^ZAx;v)v7;d3^f=OYWMPOyEe`&D{i=P#f~Y3rnJ!#Q=g(fdh|ign6)RSPrYYS z-^57cfQqUSHg)fr^ZNFYNKy>i;P8G0Wn-6)&y;CoJm8iVJU%MpJ?)%=`b@RlkbA%^ z$XG}e%E>=<8_nJk0r`r7@_?5YbU z^;tHzuAs1L{IImh;Zb=x=ETXl#rHsC-b5x|{_DhakZ5VmiL(ifp(}UU=5~f$QA|sJ z6yuWbaV_eY{))AF)L4tU(T+Or#)Oa%OYzhZ?|>;T%!OOSEOQMYi>M93)D(5;urw0# z+Za8Hw&Im@-g;#|{65UmGHr_u~_`0ozW5AJE8hzwFj%mK8&SH4z^&6_Dd!O)N&V}{~R~N*Y zn^NUY-$PM5bc@;ef7TA8iIw?_FN!wiFTEsMoyV?Ud{NeJ|L>aS_UmWYd5v2BckN}w z>!=K-m|(@qM9`#YD@CpK|F0T|w;)Dtm7^B1=$C<5m$t_Lu32Co#K>g8E;b0xjb?1w`#bMcpv0|2Ws%mv%5*UKw-j)DEZH>^*wM1I@pNx*yu+c7bFbc)b zKl2KsgD01OUaAhZ$-m@&R<+45aX+_4xSubz$>U|iI$SD$Y`zhIMAR3=<#AjmCbk() zh7~C`WK;$nvI|mt0xbgfRkzEw2c#0n=nX2V1mTkeGwZD(qZD@@1D@NBQa}PdW7rNx zZfM0!Ity@s$2@y%zs4?*VNPspEKKpWo>gLJQNzP=_p>|pG^ZR+mL~eZ)cpiS3>GGG>S#B>ybnRgu6g!i>6Fgi}37#9~#|dAa9?-gh zv9m%%PVILG+{Cp6AEdsih;tYlxNgn9Ml;b}@7}s$bW(!%j@8*E{Y&2gQ{H_S3?~&H zQ2jb;E$GW@5;&Fza;Sttz9}=ornu{=E>Q=3>e_mTw|{d})7yg!8^;bWpX-)Lw|@il z-=OV};>M6Z)=w?PbcWM%8q5|-{gz_ypo|-UWdDY5k58L+{9F1RWgNr*3->??BWF~E zO6%|+NCfmu%y<$059tpNxEp{caspjsmq$*34DK)q!(r$WiKTMGx{hYaBYcR`Aqild zX{?)t#CvO5)aq3)u5W??9I5Bcl1Jp|W%NH^+%h?*VbDnZyden>c<&rJafv0z9PI0x zF-@vkRT5eO>f6}XZ_p$(90*511(4KCHr<|avsAWzJNk6g)!+SYuG8Hzt zLXmy@goX*Xo7aqPC@pJ%PxP9Y=q)^it{gZ3q=)kI5-0VlTUvgH9LiK$cDO4S=Q6r% zFv^hKUb+@N?UGL<4`nRb zf+AWD4jYR*v`(VLY##w63aWwN2za?Xyn5;nv?FhO8GyZi0rMBkY&!&IJ@Cgry;(I5 zeT(kd`O;62{rr#leY9}3)Lu5MuF`DYdiC{#@Qby7d3)ceKfeA#$BPS&*4eH7d}wQM zFbq_I#^3({MojLral(+bLyD~$0Qg}WZjbdQM&i!WGd;LL>Dej9l&!u zAVY4}z&|Z?NI^_K%ma zV{Tgnm@PJ* z^6@cHH*w|YO!M-@JC}nh_=w(}h?FW=Cg}~L1c%JHq(c!EXyW!!ipzwWrBa!oZNJau z)#_3$sdil>=FBONkMPp5(cUY^o9xjWEXg^!3Fr><%?(6Jr{QMv%!J&WWQ#%XN%rRS zvd=M1E+Z8!n{1k+v*UbpTCa;6W7onDrEY06&&l=SUUOXOW%hJ!V=QyMy3v>vaMyS; z{UudtBj)ZuzklwCw5k$+rl-aoNHUfT#4SItM4N6eOLxub71kRS-E{#3JaQeB2cgO? zjF#B__-Lqy63g0Q#gRxcfX{41!=8+6fLpJpol@2`6sXX@0QYQr7^uLk>ui<~x^W4A z7Z2usdoe?pNz~`CM-szpBKuaNY}#xgB;F%#=~IojE^i&8Z#WyUh;HA>V_J`!iloxD zol!*0L}z`;yh^{I6*3c)%G6}JWz~DDw|o|uTWihT*ypg8Tms#(9<<)zvJInLyp~F&wFb|6OwB?fCz zx;NRVX>hqJr)0_9m1{@W+&I1}Kg}SAsvFj=yJ5__btg5CEgKvfn$>UAh&rFzkk{9j z?#%1$QK#i4*&vu+XU)xV7v`p^gy5J(H%%D2YvZJnqPp3$eBP?;6uDgQ4vY?H4FHr4 zbtSk1`MpyTO9G+bfIyWm-u6zkS-)ntz}4eA-tjhU)~h?{N)zBC=zbOM>0`SQ(vjRH z1d4`1M-NZhTpZqvg-U6N1%0OlJYz2dS}$I(1FhK=?;|ZU!C{cmx_fsk zx`>b9J5q+`u0qiR-?QLtkM3^md}?U7qQ`~1@vFp>;WaJU&1HEgJ_peJYi)H+`>;LQ z=NDTi7;m5XTdgs_t3Llh6ZF)FaxtY0<1$bqW(ubcFvCtYw?)PG&U2xe2!liqc(jeg zaKLVseB@*ehLo$}6oY|vO2&`6I${)?o(LGaNFoO&F&%BhlOCqg_(G)j-Bl?;k6ekQ2hygrlr&@mJaf^ zG2TJa#Ts%ArdgfP4l|3$(Uk~}5Jwk9ZV-;18|~0pl!@;-t$d=ZZSD9?(+oSt+%M{= z-#=!DVcMqgivGFx6PPa?k=vgoEf|^Y?bF8#_v2uD3(T~FBUUGuX+@2RX(1dY&;7UtE2cuN0TN2Q- zczV-<;@BA39Y}MX<`}S@G=g?VDi1ItegAi&S&vd{)gcdw>>gQ7rEeOQ|;Lm&au{-wdZJz|r zfkOQMT;wjMV>~*EPT~hZ6CSH?yt$^JV9lGL1drDeF)WVFj1Gpq*jX;d74+zW9s`jK z5JgFRf+HTm|LkCDB?Ko{qNrC{k?bP>K0 z`CPQ6iRyL{`Aa5Kz%ZSXqE3mCgrfHuh_s8!3yHLg5N9rWhi7VL*3`_5)X)=G^Qnl; zw_erOVzA>LsN(GO9BGW+d55H{VQKOjlo|u_Yc}dzaVNJL^*lbk5RGP-{|E6tnE`m( zV_;-pU|?Znn~>EK5YKP(m4Ta`0R%3U+O34q|NsAI;ACV2aXA>6KokHq&kFwl004N} zV_;-pU}N}qmw|zk;Xe>?GBN-~kO5O20F%B3a{zeSja18O6+sZ~d35)T@y3fGq6Q&K z#3;$e7rK#I#HAZC3j?BvxDh4bLd>f1GyD(1r5`2YE}ojHnyIc#hy#b}sjjX*_3A3Q zLx->2cdqy~Ai8-}Kqw|zLKX>d100>d2f05;+SBKY-@SYl=)BsaHNlfE<$J(a=s$@~ zkTY(uhwf_Nf1JH5HglkJ_29cByNdtEyC*-SJLiR`vZ>Ym@hmWx+D%f&8*|-}*WA^9 zC|vGPVmD@8mY3Ppm7*t+{%0 zUe3$xi>^pnz8{Jn_f~|n=1bM?e)SEqa2%j_*)p9oJzqrsHG%rowi8W>&^oC7Z^)$1?lvVE-}Lo@QHl zAL1W(+s+g7l()H$tJP;Fxojr=rqrYT|F@BFOE@$CO<+ykvB!KKV|`KCY0giue>u#( zc{#2C@38-pdEa3_E##M$xm&<)mEhC7|Heqkuc|}82FI1g#NU{8W7k|?{$C5qC--HYe_r`&3)yB3p7Z>}!j{gtvyDj>Y-#^|+ zcb0hCox*KUk_P|)U@|f?GjfE4q-ci7nHiapXUxb9%?O_SCg zYG8Tb;G)Du%tfl8)F91b_~OjPYA78lfsQP}EolwL2G@Lphxx%+urF=L7E`j?( z;zKG!3?Xg=62U>(meH3PkvJp+*@7HG0-@+oVkkdUA3BPHqf$_Xs7}=Q^3>(xZQQ|1;%Gi}-7!k%8jftj4 z3!`1w6l^}W4eN}7$E3xmW9+yToF*0$TfGXlO1sJu7aJ#uv#pL?U9;K|pSA|ErV{Uu z7vkITz*_EF{o1Dqw1kF);dP1Y6ze7usfqpTY3n_N+70Lp{0-en{z*9-IU75OP+}6X zmN@-wWePNfm{PupwyB4NB8f>Vl52DJ=Gj!)mZUUzT6vmlD{ZTh986}CyU13uCp|bl zKAn@^l&()7&cJ1qWb|!gZ*yd(WLmZdZLg;IQJ56Rj<_8)J1kTNbs!6zMadFpjb^jI z^X^RCX`o?gLYkU3xr?|;>;F+NoY zeUm&APr%dhCJOKcB?YYo1BIkQVWE9LdOv6XP?3KTv#7qvS_~;B6qgm7_)tEFuj0E8 z5Dth00RoO-^kDMA=7T^RVWslJh{N(Scv<5S-?4(12l9WjXPT@{TrT)@7spqu*^mu(jy{z7J269H(fNKypn9qXF zW}el_W`F8!6#QJ;B#?vUBzc$Ic@BL}sqj;jC~W5`=K&>EX}AErAi1D#_WVL?!M12F zVlT=rx>|XyzF&DNkSa&jc?o|>e#xTd{l?QEG+mnU%k<0cw(_=)HqRB#6?uC`yR_YV zm2g$8P0-4($*uvqC|$2^@^@tis6%)?;d+Z6uQzlu{viAb=|*?^Zm@6IdsscDo2;Aa zo8!I4Ugs_7t&Ce{1Jj^2jNLB34H&t1D0ggq@qN0!(SBloQNQsn`flrh^IqgV#UOmJ zanSXb)l_*OeP3w?n`vg%gTM#Ep|GKjhdB=?hUvq-k1&tekLthbv&337mf6Sr$AA@U zWm*+h;0fUg(^hITJrh40vLozlyTm%Z$^ke4?VW$5R_*0V?;}v*K zpFy9=pVhuh-{2Sc7t)ue|MD-B4qk@<004N}V_;-pU}|TQWKd@S0VW`31VRP|2QZ%j z02b5%5de7FjZr;I13?gdcZr%P1O*9Vb%j`1B)Ry31e;)porr>hg>XqOA0)YpcQImX zX=!ccFA#r)#?C^p@rPLXc5jnhVunmhg@kw0IK01$Tfoqc zU%OIon{O6h`;xE1J|-*RjT?!vdj8YXsmZgNfjqfHi@3S5~dxXNS36I^m8EqcU{ zbbbI=6OB6n004N}eOCpT8%NUJsur!ZyM{0`)2^f*t-?+mhnZ0sNiAutk!C!w;A6~P zIJq1%Gcz-Dj+q&9%v5h?WUs&f`+k4x?&_X?4fS4EwWfIL|NY0eNkLOQrHH5Qp1Nb| z_Nlw3?wz`i6y+#S1u9aBrm0L7nxR>mqjghvPTfCs53Q#Sw2^kB-DwZnllG#$X&>5` z_M`pj06LHkqJ!xWI+PBh!|4b*l8&OI=@>eej-%u01UivUqIp`ND%Ge?nk;J2A~oq` zI)zT9)97?MgU+N)bQYaWo9P_dLg&(XbUs}`7t%#^FVTC*4JN(>-)A-ADJ+Q|JMD zDm{&!PS2oc(zEE<^c;FFJ&&GGFQ6CFi|EDl5_&1Uj9yN!pjXnX=+*QZdM&+uf5&9^7j6P1Epik1L=+pEW z`Ye5pK2KkuFVchbCHgXbg}zE(qp#C9=$rH{`Zj%szDwVu@6!+Hhx8-*G5v&oNv%nH;ElW+@6LPhp1jx8p}aTm!~61nygwhn2l7FDFdxE)@?m^9 zAHhfRQG7HX!^iS*d_14PC-O-=&kJ1T8rNB~#SLEMCZEiw@Tq(npU!9SnY@Y5;#2{BV8*KawBCkLJhlWBGCX zczyyuk#FNC@ss&>zJu@NyZCOthwtV4_lw z{6c;aznEXbFXfl<%lQ@jN`4i;nqR}O<=64+`3?L=eiOf$-@gE!T;oc@xS>${9h%ZL9tRQr}CdQhTd?)V^vzwZA$*9jFdc2dhKWq3SSoxH>`|sg6=dt7Fu$>Ns`0IzgSN zPEzw~K~+^v)sIQYAx=G!vZc#0DtFl#FbyQaw)l+>nP>$NF zhRRhVHCCST)ixEVP(>=9dY~AOo%#7q^Qf!y^OJfZtE*XE%j$Yo>#Vl2x{=k3S>4R) zO=(@-lGZw{^_H{qeb)}d{3s5cP9ZdQ&>57>c*(e)Z}J0aN4YSvgEESi8Trv_E)GqQ z>pAYI6b)Lg9rO)HgCcAvjMy6%0yFZKOmVyCjatsQl+<1vDX-Tngie2KyQ<^$^HE@j zgWSLynUc(ATDBYIB4=cBfoFGTy592G6$9O+Nuv<^sPfLZ?X6UN*IsRPoS@?xS<^Rm zR18cnFyWwttt1n=UT2u=xpu!Shw1tQZ*0QylIO-F(~|vEG7}3-XLjrtwgnxpYl>|< zsa0h6bMimTwLNcGLNT&~Vcrj%aa8EoBNN!Uo;Qxrx&7@|>j3X0N(nf&cv#Gr`4kM?xn!{Nt&bTY%Qe0*yW9NEy$G~f? zC8uk=qVIH~I4}j@j60579@%~ido@A9?qWjmuQi5?0EtDXOiKQMlw^@$eXRE6V1pvOM#c3e0I`E zjxg=JaoB<|$|Gl-nUz#TiCy%DNj9SKaytcuWtjcFJi*9*;zcxCL2`^oUU_;YMZ9oseIt{oHtd))O##f~=` z3CD$z-5;B%Jn>iT@9-n`CvuOLjfrOE=)R9BJ91%XdZI!Tq>ELu2DY#++xU_RB1cx- zkhKS1;A|K9+U~R{zSS9El4#k9M3<@KAu`B5Y0adHZ^`0;r-o)VC$~8)Wm^tsqd`1s zhq6~VZe7;GcF~?r0?EL3dzB=*q%oz4c_l>5y3Tkg;!Isx^y6?K$C{PfV*&{qEqqQw zh%+w8;{IT@(syKqcB+FkI$)W+D>@M8;=WfBiKh$AO)hWREGGlf#j*pJCTA_AGZ*49 zVn{_KCYJ^d?y4XR)u1bvLewD68|T`_bt@gXwI_~^OnD$QX6jB%sI8b-v7h$9AsbRf zwstCV<1RhP1nYL`iv3+dm_}l_*EWUaK<@k?AKBqBEJ#F^!%VjW$MiaOXv$D-dQbBG zz>EDHe3=)G#N9&M*b*UBCys@Nt+6y+EWUMS4#XOD@kOvn5GoqP3jt+Y`a` zMgLt%No`L!u4Hn?$eD?>lZ+xUJ`%k~Mq+D8v>gcdwnRjUd1V)yXo)P^C5a2dbKlG* zE^bXS*i70?m0Cn9ZH>AW!A1iw6z7{#7&{RdD?wCPvCxr3WsGDPPogq1Ws**Cgm&z> za)N$Iz&`TMv^|p5?QzExMy5M-qDl{2l2x`E*}9QDFi68xZ@yM`S&%N><`z(9!lK(V! zqj+lY^0ZT%=akt@JG>+U63oPEQVmIwg>Tb(D63Zs@o-`=G z+gCB2Re@72bCbur{B_EKIZ^^kPAfL`t}wd3%52tD)0spy&47*($S2%%vwRidv+0G2l%L^T!N@gXa`J zt|{3iv|v+?u%Dc+botAZOjmB{v8>qoR>gsL(Ztooa}Cyry37_bI-MDE)V%p^?^HW%Mek)o#@n%rtn~*LK@x{`ojx@g7UMt!j`?QC7>(%&B z$2(z%6C$@R=9_mit?KyP*!f2mnzcOSf3xk*iLkY|?(A4>KB?eVpR(|~pY^*7*4*?g z7iuep%c$p7n=YKwG2OjP_ILJv zr|{R;w_MiVr*l3g-%{t4DX-1)+0(lP*Pk$(YgXiK5%X1bWo4m2UU#cuC0|F#9w+}p zo3e{ECLB;c9-hdPrMtRA-u&F8z_&ZjdmsL@sqogkKLrw}=ksKQJfF0AyIQ+@d~JV; z_vAURmszsUU$b+a_}ZTh`;N|3t?W9z+T`ZsFFNPWFPo|RGNbavszoanGK6Z-E39SJ;) zNkd9QERbP~K|fQxI71Xe#=<_Q#SBS|9jppsoA%DNoqzQ}Xya<8aMpEPF`_%P3PK;O zidfk;HOt{j!wSa0)7!RN&Mx@u6sE4sur}2@?^ z8#Wv}By~Bf!NfsIfp-F%2lJARq1+r0sD1m@v?tOIVa|WvB(^#yUwRlKiEL5%B-7aSVOdGDE4Tz?STjD?ZQn8?U@X)9|BYs-XttGS%G6k19) zHZZ)DTJoArfLFm`7aNe7Jz81_!itTT%&fM`8Do zgetlXfhX-f>pHa>CezJ5a+CKJB5E?t-D3Q@I zv;Az_{%F*wqQWVk+*x^)@=9sx>ldws&U_`?fwx|)6i0%hGq@6No|Wjj+Lhc2#LbXI zik@&>S#lthOy5xS4viawbfqcF5t#22r#4c;ULsQqOn&iMQrAORQWXh`G=YxhM*4YN zTfgWxZlU6?d>wP(yNq!jqfNVxB}>Ww7cSen4lE1$g!lMN&~*PN_7ITCO&u%|6=U~^ zD`NV@*N5j%{d4(V*d&F9*Lp4o^=-wV4E$&&XJX#);dbqZ^8pUYCyEa?qdKs=!}D|N zZKGn0G1#bWFe1l-8nC}AR*a~P9;0KUBrGsNR8Um3F%kp&^sGD!?K|!B(qItgwkPpO z4nOg8&Z#<)4^Bj%sQjrANfD$Zj098^i(7$$Vl;{o&HR7r?C&hE&b-&}y`y4mHj%mu zNlfW!ecOyC;56fuZ7e6t7R&P^z1O9)e^Pe=qGENxwk%7Q3&sYU;&zJz+X!u6Ex^F$ zTu6(Z`;JIR{;Knn>IcTcKbV%&ZSxB`P>8MADLLm#sD>oQy@;IWvGh3j=*Qa5&VIQ& z#BvplZofSw5gN50lul%1ZW|#duBPzgJG1nxIGMaB*-obI9wC1%7zRoi%C^%k;Mn?+ z?pUuq3@j1^4v?E3B49cgqW>EY2?-#3jqje^;JgycOCcwp0HG~LNR*rji6bO_n_6Fl zxt$OawF6EyR#iAg$gdotjwKXO)cf75+S~gE2n>cpa0mh<1W_5Hw7c36opP+~qRPFS z?z(HcYuX#9GugKj(K=EQB_0sAfiipahu*36k{xIzyD2!y5%vK1@c|DQ3Q0^$kT!Po zBklXM?*0ZWJJ6;!hoDZHGR|mrw+{{o{_lUy{_6}+Pm!l|BNl}Q;&@bv@2Wy(0-c_O zab6Z9oUWgiKYRW)Vv0%P;3X|rT9E6xVx&Q%6AWJDG0oX-H5vJ?>5A8;PEnm%C;H~y z%@URb{E<@x+!!CGA#@@j24G?{>Gvg*2lVeVHM;^7(Pnl#tDV)(Y|gCiIh;CbXJ$WV za+~#V|9GDufDe2U{2(L>iu$ z&FbBmZ9gV+TlVF2nNyNeYL2HloUh~eKdpS)>J9Pm#Xd(4%myqFVno%qUa9n|Ua803 z8#-)?GmgDZL7HHzH4B_FHnRat`EXP62|?edFIDRb!q%9yytA|?Ib5`-)rNGqg%GbH z-}d(Uw;KH$fouQgEh;fvK+gfZPMGsl{cktu>gD1?zL z`z7_05U{qkjReFC1qI#x+jpODe!iG=?eIufIBbyAS`i6yq~pK;J!P{R?B6jf<_85Y z$&N8sKi05v?h+0-IZ#Z-(g8koZ#f{v7%?Dp!%F^s91LTw|BvSLb7Oj@878i9HK*kSp)6{%ZXlv-PQ)RD zE`x4f_xM$H9{@mn{1`uWwLbR;xgELO9FcMuRbkvnQXmT&j}ZE~*Z9?u0F(1c4Md6G z%ZpLJy?$`%3V_^=J3F{;`T31Z7#Ad=bomK731~(`S)uLTR8OErP908ueHZaDB4D$q z{GZri&j-sW%|A#W5to*SAH-ai&E<86{%v3LDwPh%=3Mm7wrS#iOV1$&8oKgshx_jMlowl4ED4$f#L1!t6C1g9p~=ODPt z5-F*yQZ*RmNQ`~4r~k{Ouxs3@+Z>Q5N}1kIzW_;y+Y`2(U+=Sj1(9)2Vkg!}$DaT~ zSw&5w0~|KUc7%a7st`^}4doR9Pl!$j8b%9FcqlQFIssg|->XC5YmQ@}VmJj+^a&GW z;TT&?6ewkE94j()E$+}^)|h0Xjx{@?P9)U!BBDsDj}WU31 zAtcV{=d|bI-bs8=m>_-=CKKcXWW_GX0~^$^=>jcb2lM)283`*Z!V{7?x-M-}_~|s` zV|lNhxg(2J)xt(s?g(|g4crMAX)o}cuastffHd9kY=i3#SX1;l!-O06F-4v5y)!_N z{n~32h};!G7bhd5ytZSkz1eQ+sUW)X74K7DJFF%9?n#Q!!7ID?F7r$p*h2z%vFq+0 z9=`hOhOu`E+Rawmf`Ea#sNtl*!}&#cW`0Ouz3DI?ydh+i=s;0>PiQfT7Zu*A>rw!Z2oWMZdTlLANQLT4}czIhYZic*axDrD;QpTldic#?)QnYZQ#V&@GPdWKu$ce zkR96D(D?F+uOEL7E{&8{@#anN+7VOiE7M#=o-3l-Qlfm(Hnj`lCvjX<;N1eImGc}P zIfq1q23S0QB<*mCfZhipyXl3dlKdo_(zgrVEctLByL0)aRMXBH-Ttp)yZ_WqYe|tF zU*@4;)#eID=!hTcSCgMs|CA-!(RT=~eyOCyMAVSk!pq$%^Rswq@*cQ(TXI^ehX9#d zQzf)Vo7@<4U`9OSg`E*=es@n8G*SbT@I9!qVekl|qYka=BE@A6$s=C?(x-c+DlyNW} z6eaQe@Drh#XmE?Ex(!VKoZcdgD?X0w=CviN3tmmjikMECbJNHMagMY-l@hQIzV7AZ zriQRf5j1k=Eh_KlCFt5{BiAK6a8T){lxWsNJ@?M~+S(158s#PwDXC&%gvLuu_&~q; zp5%18A)_>(Gy@` zHu}fy7?5gdqUqRaZ9G+VYFVjT`f3hBTtJLx%QHo4W^k7Hn4dbj+U@EPSKG&~pSs!K zvyPmU&Tyr~vom3Dulo^!F^FVgi})a%1Gn9)rTvJRN`lw2KOkz(aW}5MO~dBSW@edL zwPwp4)N=wJup1;S7@U)OkZj2gQGo~o4#o=@iYEeNjFZoLvW2r$?(LKzQYnI52$jlzP&K3-Fs?@ z8TYz{a*Ip6o|)y)qHif|*~IjRGj3tOR55>Cr^87ZMJVZQz4x-c--DZz!bJ3J`mBFt zv$MzMB*TT@cUYc?%vG%XC_t5juJ=v#VIpp<4lLvW$%%|VH?JfU3&D=q@FkudiARUh(d2N+ zWLd~2X5t4S?fb`JHk6Khs0b;)4m))>Bf>MuG>~md#IxJ@3UBxJiBI@&t;m6*b~tLF z>Y4m_C`-#PTHIv21B#D$$;E^HZ8uiYUtFhV*G%O%3~-xR^LiE@?1e}-zAdW`mbEM> zF-u5dt!0p?EOIRw9HXESaG^}g@5b$*Gd<>1m;%N!sdSMt*}PbmYdWd4wf_iOfHlC+ za|MYGa1MylQ*%_SxCI*3>pCu7wYNkflt8fcEw)9s%#j8m5R?-^jqs5&y2-XJ@J1PZ zvCEQxGD63Ll8sRsnbjBI1u1mJ!>4@OBQ%73++6qLsDSXuV7F#t5G=NzBh&|HiRm#q z*)7%le!&>OD#^0421Im4)tJOE2i~}o^A-DsEaeX+t0KZ z{sQInfSneVRDtp{f^<>g*rTZi2sAuCI!Z9Zh$ZFSky>G5VCcOA>UPbn{DxunR4-Zq z0{Rr3Vcwm`(344N37c0jkQV&${exerkPtp8!}^!LNFtPq`QzzulIshDd^c?rMzvmA z&&_^jixC$vO7ZGm0Le*_7u+*exgqHorQCbdJY~!;JgCi-!q5HtGLD2^A9dP#_`PVfh~Qf+*{6POoKUi6l2P%*Hl&QKAyfLqkaIKd`D8JY1@={Zhq*1zZjQU5-VVG9EdQhh(N}S^W*!YLJe?QZ~`l?e_yw z5+Rt%0P61dAXbLEnF=K$2o+w?V3$raPx6eS5Bi3KtXuINb~@n7ggV*iUfP^;*T3fx zK(YWg|IErMMW^{br`nI~*hvLG+;Qa(JTE9Xz2mD|`K zWkMsBLSxbz*}wwmYD`=a5~IW|zFKINTi5zYJdLXS5AlQ;aj16QewJ%pn@7XW)l@{k zKU1m8+14)_#x2y>CEb#Vl-cMv42b@BrfGab7RyPY#BuR=W2k^v0h<(f44SbZ&kQd& z1c7+0f=Eva?9UId@{fgyyLhy>XLZ>Hs_gVQ>JLK39^$?US5+# zF8FwgP0>wLKjyriCrA1t{C?ppovgaV>1c~smv@h!4uR$(`2`$DeE7c~B> zpO)wsEU7ZQ#)-uJ6()96NKJ8Y@H7-Z0#aPGy|SvlSYbSo*fbFCmK;D$X{<=pL|?w> z37bU`XR6OqiFvV2n$yv2RQ}kYO5LsvtCo2WW6I7VnMg|XEFd+Y{o1b`B?Ku6B<2+= z&U7;n*3GsPjMqSY02HvKv_gCJS?}VwnX)lP$9Q?8>7cln_TCYaRXg*#;^hb%1uH+IT+qbi5QUIEkAPwUL- zZcK{joDF?6iF-BK80ny(qch>Bj2#sVh;E9olq4i9E2BhC2h@ZuNbOcWnAb?Aj+ol{ zPjg%dw*~)|Ezvu`S2h4n_?1nG-8izHMroCi)H}Y7r8gOC^D?nEB?8ux%nux4T`W2w zjmomxy+te?pWb^_g#G~wZee%3vH68gXQ75Jt@23+IdVE`poA6wl8hR#JV_HpwK4Eu zBw$Qpa>tT{f!Cet&Rr4Zc;X#7JyIEVCMr=i=zs(;dVe1C%lLUbh~NS0gJ4a3_SBi0 zWKV|KrDg~RR0H=-#?#LMUi65trDJ==U20Be7 z%Xwpj z8rGRuVi>6*eIn2 z4sdTqnx|BWhY_zMYaCA7zUpjza))jPvt-vupa&k7+<6n*ist$5`NN|BwO~KBX%LYryjwYCD`L@BOz&Y#&6yLk zrl09#3<5$~a4xgYhziDTTr}+GvxUZ_irgNJWb6?^#5mb!Oz(fO^4&7G%H z5^GS_GXIRAC_Q6#bn~Jjo?A1S$rmQJt!U~*P6dbvJ-70Rj*C#qoAg1nM--Cz!Y317 z=u#u7#!Wgd*X$9WGk^)j?$&fleixkNGkSM;Ai$K^JD4}R=>kur91A#{$yq51$wX5{ z_^yQCFMy;I)XX=RX%FBGjUjh=$~M62v?QPtjW|Ux>QrIgjQe~*2*&>nXZq^b5AiNL zZOI)6wC_3KIl*(?NODXbHzum22a=JFGaEv41mKQ*TW=5nCK7LT+EZuu)vXw=D|?|q zMZe$WYg*z7q#{n@ie%~;HG`r$nwUvewW8XJl|HLR?P9D;g~!gQW+^ITmZnEFJoC&$ zpqK!kl`d!W6#u8;k_s8NrGXb9K``UKExyy)qZX#Ac7FthR3Nwo1`lL3ODL!o z#aVG+vZ|XXb=~EAEWJ7~DkOX|><)vPi!TI8y2~t+U`4!!=-3qTcu*UzvmX| zU;vxoFY7w$fXLF*)+alS*@;#LhY>_6%d`y63v$W)kPx*5f^bYS(x#$=iQiEsSbWTj#TRZs?$7t8|iN~L%c(PyNt zN>cc8olk|i&vOa$9mc_tq1qTUO?Q~7+#U@N=prKaG!!!T;ppICO~e}UM7l3dA&J#? zf-}{*xAKAEE{qjsE0aKYPnTB6aq63DUe`n4s;NtDuJ@l2EaI^^NCY{ITBxi%Cb)05 zg&!!x67sqr4))=f2=^B;|&U9nAtxK%O?JrH(qLN-KLYGA2ys`5Pbca_F5=9yX0 zI@KWOZ;?E|06C&Ni~*hajz+-M`jaFaJ2KXs*J`w}5c=M_?075|63ZIOft^DH#ZttH zbQl)6uo5JL99BwZ9>Hda#W}|*0Iy-0IZ%nKCgAwd#WqiGzSaX5Y^gk*)brv38S)wL zWOF?u0W-yO7LT=1Ezn{_pw#>#jSuWwImbE(F^wt}}lf1z<$?f+@!t&&enhvFSp|oAa+s9!U zHXe30?GjS`pv=ByF^BCWSWJbRy2A=eiD6-y5fj~pEXMQfgpkY{A~P+|N8}+K%cVH8 zxAHg&eBe|%Q{GUMi~=9Hw)OFF98FTLS>9sw=B0b@E4xqqW!sxF_VU+f1*fUgb*|_4 zRz3PvJ}t!oYhpH4pAwRi(5Y}*;!VBKPpDx3vfLzB=tRMJ8;%jV@j>6aqg%i<1&#b+ zk^D-3Kdxp(KRuW4k%?rmuP94I&g0b4>O%zd6?@oyO6liO1^U`$YEO(w~dfSW-)I*JFbc95RKnhH_Ueo)^V z5O<-H?_2BbD+u?V6s?hlkNW{&D{7-4R^P`fkDgL0;{mp{b)#&5Aruay{_1@GD<`i@ zS^hSgHnz=Q2J4n}WYT?K1Ba~KTmN}=+nAMVj->#wyKf}M<5@kRd1_Le5osxl7MTWO zkkpGzVMHjsSp8MXcS#7V+PhkS79{jH0@}OoIU2e8CV!dMG+M*m)+daUL`I+W-4I(& zUB!OpWEez0R`B*0QI%Jr&CRlbeRfkm!A=eXZTHE;D+5#BaqzefNU;B5|N6>RA@|Ob zujYmt7m3)_czpI-ihZS1NN z{mBusZ?O_Oo54A_*Q29z84jB*6Wst#IvTqXn1FOd0WHRQYg4!CYPDfB?VoaEw10XJ zM*G{lAl|>>gn0kjc8K>kTL8Snq(eBCBR95iHQy_>TsDaOw3GMV`td+(amo3Y-6~SVgFExhSbYQt48O)0=vGOBz@93V1J{b z%hnjMkz5Lb^ba^Q<`P+L@G)XOzkbHOO0N0Xg0Ihy$^3ajb3G!GhUm=0X6-0?ONj*> z_f3DrB8?gdNMPm0cL=p(y+ve&>N;XLt~MwFIj|UsJns<6WB+W8-IyLPg}oO15Nn;A zXX*?`q_n+^0gs7HP%P#UtYbBYu|?p@^*>8)y$gH5q(rM|2sDE3?Nr_ z6;wk|U!eBTYxBbDj4oegyx`H4PD;~E0DDx)A+w4$lWIO__?$4^47wxdhTYj)uj=EM znyJ8s%uB-ov3ip%{vp~EGl-_rGMMKEfwnp}WIi3G1!!q)Mb=!*J@7~jy3`z6D|(ulUfoM`T~yvcgH%qlR3L>cQz}3KH_#K=7el_UiNveh$%U8? z_LGuK4xOlJQHD;H94v&y2_rh?&Qj5;yNIP~_>vbFIhO?$;xT|Nf?1iDP{&TfzW|C{ zCb@Y`IIq*W&G(5WFw0|-!FC7~@WzQ;j=+kc@=CQq%FR2Z@=-e+m0g92{YkVJKEF#;crZ%nQcFJ%ER9s%lZuHyt zzJCQXZKOUpq-8^{@!U>*5UtJX?PJ5B=GmY497K(+_9#(mFzjTf_-f`njzVGrbu~ zIo%B~2+9wdNd~?$Ckbz>{gcoZ5?p1VB{W_&eWQl99s=eyg47Eg{UFjXJqPm>4W7YD z$9-*oALJ8xuo5PzsHx8)k^U}Y)`AIEyYYQx=Stt&>pC^1 z<1Ipzi|(09mqxhhS;O1DqBDH|#e6Brh?)T?##hqzUdF1q6jPRD!uP? zbWjmu@AiW4LERk~L~lO?LlBOkXS8(lwDr(C^0>rF%Uwqug_tr@MLb@WZA&whtoIbB zE8!EYJKqhOTZ^g|%QMT``HvY}F|fSBy?KOoxP^}j7bAZUs@!njJZjWwL(^eq=6+n~ z8%LxAL!~qu?!w+=bz*cNLZC~R!u8OxQEj~wJTO)h@b)gBEo@zQDyI4YXo5}-(Ea; zYM(shM=smh)qbs|w%6;$>GU<*xxL%3UDH z0vH0D^OBr9a`sG=$rh?)7@YIo7tGXb<&x^?G`z4x$kihn?Wt54!tl=`j5ks~^J>k@Dr0)P<4=`SHK z9HqZCbCIW(RVN`J;D75Pe20ytLgS&Ts0!l`bX*&cR3jPU^U~6tO^zfhGHzeRUZ*DYv5=CgnUBb27sKfkX_*_QW8g{ZJrxy%`UQ0*MHZ%`jL5C?){`F! z&C1heYOrD0xYm%Mlg`aWz|)=J6XL61(PaYmoZu*Oee#}dZ#fyd`&CdjdPpQ^urvhm z*}68VQ1kadK;l>pC^5~>n9Trx;doyON_o9|l{4Dr69cU$EWU&B<4x-^ZkyN@g+6xh zPwMoB)w72E_{3`d-x8SCuyV~Y<7PBtbGlz8b|q|+<4fOKPHB=WR`~8S-zT@E#MIz^ z=alPCn@!+HKuGW89YXG6E7SeT?x%L$Rz`6^7@OU(bxT^EXsU2P?CnJ`_xORo0LS5ZqJMxCVbRWeo-#hK z{zFi%iIA{N#Sai5nrc7MZU}T|<(}BnT?3{T;ZumX`1pI_wN=xH1(7Hxv$bO9qbFvM z=4UX|gWc*FmBdU?L8VP}WEBU@DdV#;!@A>HA=Y*PjwWDlg|GfH5>Q(U8=Ya^l!UuA z`@jrShkPR|fU*HMN(H2f3L_iHxXfRx)nrwvq&6c~8APszz?(uMOM~~;e4-k-z`+?7 zfGGlRkkAmSbZh-=1DfW@EUpy$Y!T?8>kso)AM7dJxn-C&fjmLF2(TVpFr4e2U+g#7 z+4k*TetXy?4RKO}&ah^a69N0{Pzn%X8X;zvwD}fTRfDp#XjmKaqHNo}UcvD?D4zpu zpg)quKs{n;XPMnk&6ayDlWEX8k|(r56^l4OXTtD$NJe@v5fJxV4@4v5kU@+YF81KM zB`3Ckcdb1#4>KC1$+)+jS|{?MNO*>ms=Mx+CI?BKk~GjUN$;IXX{4>cn`P*Fl-e82 z)6I{U{cqygw40B6gQ97V*DIRULB6*KLPT`CR2Q|GilRB@t|Z3gvZLw#C-?I9 zy!hb|Fjj~seB&a|1(KNJ>wxs3916gZ*He~34@x1F)sNqi(l*9MHd0)QHWXaHyE(K7 z7cKZ-J*L4?vm!Z3S1w#G4ti~Cddo)5wN>F(8-aiB*r&s{6%BN!A zfXYqSk3jA<$0DOjjri6<$##L%7TK|6qVIW0hR0*(fg#o6fLB0H$oz`;1a}}DIS=m zbyp1H(H}*@XgRD90l;D@8c^gVE|w&ON1VYZKqwZG5%G1S)>4fd>}E_8%j0} z>CWmY4@fF`)8Fw6=$}2#(#%l{FRR_s*mX%Ry$HHIkK6B%!5A!-uyP}Uc?5jE0|so# zJYf39QTYezJ;eLe`Rl1hBpc|f(m|4R>6nc&+U%5MHUVSI^MY5$rR0aBG=BCa?{*tv z8T?`Y(3M|9)vn`N-fV}=sLpm8aiki6a}XqLIP~HXQxETrC1SUhA1v?k|2gmVR&_R2s(seFN2Y%r46JqWZi{zMzO@6d9I)pcW^+TATpWS22)!K7 z{@c%I{Tj3rhq(T^vsRbu&Ze%9K%2Jx;;cHVUtnV^eewPNOqD#*TeOfPRjbx2AAHc} zt-4#2+gs(Qnd`dLr*F8*$-Dx&zg#^>Qus?OAzM6)zDVOgj)gmgIpO%m1%Wz|)Je^w zE56KO{+Rh8zqjowkH|kGk|#&d2je}T?ZiXYJha&VyO4V8#=E9bh(Tco8rT zPe-~LXJF3m-dlc?;6F}7;88&8_{fAd=8#U#frP4_L49h#jzVGc!5lN~#ic3g6~oWV zv^sIRNviD2sp=g0o*CI#Z^KCv z#FxvQ-B_rBq7Gjt0mKsW!!`BC6$k3Nbv~=i32Sh;2_&#wx~G` z(eO_m^%*b>b$6$%N#e-yrUExgrg)Xbt1_?iT*?_%W<73Jkye1Kq|hQGIg_l`b~tzn z`?hTr4-{}gX!g?+=y~FiGlIKtQ3(zuiP@z5*mQMqJp{b_?lasFliFvhEL3A?EU$@}>?(xy?0}JwQH8W)@ zgM%@G>PXH-ueM<_`@adULW)`<8U01d5R+zQxRm%!F$xyv|chrOou44}{FQ zu6YqRf~q96u+ODLO0G^H%4Fs2B8k-be>oiK3g$C0AW6*^ms%)ZC=G0PHVrTJK#p08 zLXKYE*x7xsPgH(6W4>d;@{V2knw5LvDa+k`?zu!b?IaU>6Z`Pq6UTXDmMjv=q=0+& zbV0gTGkOq6NxG|T!|+7LG~A?B1pV4nGi0U@Nzx9T^F)#<4HAstN!zTAE&*ige(75b zE&EHBUNV4MV+@np3f(yUgLS?vS?RQ1T-jfytki+QU-&E97h_7L+8iXKTrxUZSLO`W zV$?#Q?RP!b+FLOvP6MA=R(dp(9y_!AD3@k>PN&3w;8lV1W+;Df)|ucTc-JF?m*BR~ zOsPF17R8HHWkv%j8E+8z^ns8d>p9D}&pP2~Dkoz~<@M#QkC?n$ z&e?ks$b<$?W~FX=nO!(W5x+0$ryG2dx-rUj?F|2CK-5Y)v02RT)wWJ`+B%|S>gH%j ztfKJtZwjIKzq@q2O_0W5goIMejlWX#_i4d8d`{b6P$HnB{fI(9u(`CzAZ=h_p7o2O zI!*lxi_iiR31c$L#i%^U6{h{zleCsq2#-&VQv#A)oq+%)VO&84x^U<84CMIggs<|k zy=BH+=Ey;ktf{G+F3hldr`GGNcZSEmemrDYNoc|SQck^RYZ`Xo=5O44Zl=_nqJ53m z?jA^dWvppdl~<{u*c`_{q0Ag3%_vJcw7Cau9bggfCgx23cwR=Xk^w6xrQHLW>mJ6~ zoLc6EiL#W%j~X5^KVItxMGgd}D4^Y)9{5DysmOKYi5BuUui;d}nD6_L6YasFOjC}# zHczo(ZSUG->j%o24td8i_|W>9e3D++Qxe`w@T9$cDvUBrFU6PyDH+cIXb67yo5J#3 zG40794Me%jg^c&;B&HbEF_T9x&XsSefG`7I4C>qZhx=cAaV){D41BBnVE){<2L>v7 z@O+e}#wYA`9CLORgK8)rap0>`tBHC{KGDrK|BkwuzlaI=96JbeGJ_Pwi(vS%g;$GU z{Zx5S_h+a9Wo0lHhxZH-?es7(>U}TAl)Q~QXj^ng`9!-l)?P)w#v|is_sESpWZ=t+AIf!#G5rs&Syz>JIdC**R%{28T7 z3V@q>j&C4r)}lPRp4ColvW%S&W~ir4e=5v=&{fKhhgb93U!Md&2bOjoJ19Yb8HK3L zy4q61UjHC7w>>t}Ha#-tZtH%1W3Rmx2ar!UlUNLfmEdH$tN}_H)_jlNOi-NOoqi9^ zg{k`SIGQU_MC|n7T(8vT(ya@_ty9AnT&F$vRoQmT4Nc^QnjT{!Vf(8~JI_I`92Py) zsKlD7l)2VxfdNW{PJnQm=uIU-Qee^9h&$N%C=>g=hc&|xSDL-sJ+%mnhFKt;XD#Gj z2zE4q&{%)2*@^mvO4vZ|*FE@S$1}z1{Oo{4vd%e)yV|NLF_6$95=Yw_z4vQ4lC3tBMDGfINUylPM{vLdC8$PvGww3M z#7!FCN}^#}-qt^>V~yZ$FrFzti)i5lP8Wc{b)L^3ngy~Q{tIn0A4raVvcVtQ$}w_8 z{3pGv*4Hunp5VvTf00XaophUX0ZP&+jLmekkfXZY#_;M=VNVsAyL*H&%BP~bR*Q}dWg0oT^8Hb z+8?1G&z0BSPn^-$hiXOPI+G&__cnoUIy{k1=Mc@&b;oJ3rj6kk$$N!*-WU(H*D=bT zr0V|Tqw7^x$?|Od3@g!L!cOqQSF7ZW$!NRFDNm;|d2K~(*`%*Q*3~y3q@}A_QE>1T z_6D(LLad5BIEtTzyE_8L9|e!)^p^N1XG>BwZkhJX2IjpB!BjvAu5P?4wikmTJr-d# ze~F%~qM?I`uv&gYSC`RHUPM?eSZ1ec==@HA#jy~*aWwx=5(dFZKo$AuQ_>Rp!25mj zSZFWpKHMx~mgDF1I61Y+^zJP>M|=fW1(A{|-QHr~ANxVa>i9KBlioZk*_GScI>eu& z1|bw(XKH?{PY2&7|BF?JPV1t%IM>@CuK1MYhZAS<3|$8;R~lD;C|B%GHu9HNvEw0;77(X?22w1IM z%aiOB(=+-KA2<0vs~0Nfhj)MhXFr;#l`0{U>G=9ec~qi63stjc&eM9u(Mj>TmCs)n zqy~jI(kAj;bc_&x@JKEnS@BxtC^T6o>twE#!UOw>4wdD*?dko{h9uAd6M2~^-V^XtQB8iDT>SuRV5`lF@KVqR6BpM!C7IOSK==Vpw&g(pxj3)fUkzqW=b~T@qFwtEZ zW+hV>@`(tZVIO~PD)HCr*ovK<9kXxHykgqU{en1fN;#jwg4p7qn!+cTEpyI5hH}vG z>x6~8sZ_AKr9oJMqy|Y0(OfufU3-I1W($>IBOJ=s6IioUUS_%(HTTpfCmY%9#O%-* z7Wh}nGS9alcExi=;#_~8?TAqrbG4o*nahwsLFg1}QWPF4TIl>4u;pQqh|II-98+uo z(Uzi8j9bgxoMgNzDV@owyPUubP~^g*#Jxy#7^83fyfvKkIEl$Fgu-3GXv3c-G_7y!TzN53|0z0QrgQ7caCIUODsHrJxMO^Wb*kGR?`kWpC;A=J&>1(h7!{7l6brcI(kLf%V{TT2<75-6 z8&zYT427ft`=>CKA>vVv&c z>9c-_$@t1_qhpRP6z0#+ww!e6an%ezStolEC*FwaLF8jo@%>hTO&IniscS@-4Xk^{ zrtKJ5&7a4q|Ll#BJS?d+UDhcz~oPM2|KSxUs4*+p8fP(ywu!Bkt8%c6sw78 zWyNMQf4$PiP-wJBw)J zFrI&zxy$w&L>{f?;zPdE1W50pp&X*=#w>q9Fo{|y964+OygHpN!b_)=H+o!D;6hCIj zaWcvUbE@H&Wtj%YJiK-AP$vs@i<*4hd0{uunqN#iOC>hj6>gO$NE&}#blRdD+`i|#RqLfDYEs|E;WZS(Jd4JuKXL$d|7$*@si*w5&^NgZ;jfd9P&&PAfyK0 z@-#u^rMW!<3dHgDRD+nfKzz(tB&HQ<8g4F2+(~@yQiKAa_dwrJf`{u|5QPP|UW&x-B%aYvU?T(iBW85A*9V0nld}B|2ByRyeWvN&^j9@JKZ@!Qbsb8_^ zONlcJ=M0REj)N6&mU~$eu?2^f;T}P5TkRP+t4-So4XIQpAtJu020vP`T?2z@1x3Vd zvJ1qX!amg}mWG+-dq>E0of@wos@EzJey05Ent8dE>tKl|t3mre*_a~%{M0D|w-9f} zC?w+bfEz#g9_ATATsZS!`bnjtFS^eH6s zdY{~Fa>v+oy@j+DD2O^9u(yLph#W_UVr5pQccN(|L%vTj^!N}UkkH#>=UUua>^w(f zJbJADK(RUlt4b}v)x_UlVCbm>IDnyO(zDGhZ+jkL3o0&`h0 z@{No_wWBu{*EDzEFzZK`(=~~~dX2&bK`()oMNe|h|4Dlo1x#xHR(r?t-E^1H#SqLUK8XTlHbx)yx-zJV%;W zKH0>$zqd^jvt0{Zv#3t^*dDNRu~*%VWSum|q z51|7P!|^AB8yP?XE}H1sStdAo3W_XgHx(MPwWI3&GkMs-JB@+sRef+T-$|bg0qg$@ zcvks%*4}As_(r{2#p-68|I7JkSlVNUnAGeZE@BMm>Ov~4d?vr*k9=pVw`DKNYshuG z{&rknNQbtbo??Qa3K@Uo4zmWL7IK@zzE~4tS9XEc*vZt)r;Y|JJv<;-Pq|0 z%OO{|+~4Q~2Y_nK%zLWsoY`7QB;R_zdr#gJaIYRa=XjEGnV2kj4}%4b7WKja_3cjMco6HoZV~yG2pj)qF`7L zVJc{QADVF*X?0cOT;3WMsv=DOy3n*h`BatGSlLolhrUJwXZBrl<;2|=MZwM#05d?$ zzq2)~RxsboSgg_(FUIe6>$S#fx_X73LiM~S2ib$bO1gL%8=}nT-y8|%NqY0{0f5ps z`ihbDjgrz?{)Wz#?J;z;zqWa=h_}v~Uwwh0e6)CN<68v4cmhg&di-qj$o@o|*H)MN zhH~@QV{>G4ak_TpTan|pCJ~N~V4rVQwtu+3Z0kPcpe!WQvt4J6;&li^~|lB(=48NU`r2 z$5ptqRbX95wQEDI>V|^m?Dw++2AZ+`PnhjdQ-wp7;&+p8j}{AOe&HW^M>tULnR|Ok zuD>oM_4^m!6*k2o77=|29Aq>saUVY9U>1M`Y;3hvO+r$Wxlm;ShBD?sjWJS$x#CFt zalGMd2ttrizow=n(pRG;iN|8%w`f9%viT0fnpPY@C_nri9kzc)_XwUrm{EN^M?~~8 z9KsqptPf>CkY>~*A_I*VIO4tc$c;w&m!_F!^Xs=YV7%&ksTIJ23`_L&b#~lbrq5XC zwJVsP@(gweY7>RvwgO%>J>JhSGf$I)DB$V(zS=M?Nr#PQOVRaGpb^N&Z?Kz!PpG`j zY2z{z2Er-Wh6fb0NAky>3RpbR633Wj$86{78f~M+Q_WnU=k|wC%-kU%`fqsdB*QBV z7l{ai1U_VJ?Zx0LjOU$ViklGOPDxDz7Q{@2g^ zTzoYk-lO!p*rq7Q`jeoGlGu3*@oJ@Ulo@R(vh4SO=F>b}N0A8?-ZIw*>G5P#o*45` zoR=`K^ynmrr?zg-4U}@Yt^%@cxh{CkoMm5 zoPXV&&8X3vA}~MBUNYsjSVrfKEPHdn=5k+U5I|P0`W2GF@sfF;XNZy%{u&bu&Q8i- z=V|l^j+gs)0&%@NSlY-OMMQ(3T%oOEF&Z96qmn4Lq!5jYQghe9lB!h2%iZ)m8(i9n zQU3Xn0y1<|34=SAp9^4;)!bVf2iYvJ>OpJ1qf4XeVnl2s<6=0?EM1vtT&$b1{(Ngg ziP`1QcuaAAau(eR)Xs)Je2aR_jJpp)irmA=VV~$?#P>g8-w^PChhYw9GrTaM=nm53 zC<$un+#*J`K`QNg-=oW9v|YuSD_BV8lzPB(|Jl~}3*`%1sRC2!;!GV6;0|>541kSrttz3llsEV32psoEb>y#`{&)#REmCm={YP3 zkS~Izr@rF*wXZJjgaYCHsz`u-g(1b@h09>l*8)ZPyAQk=cp3W?_!Lk1+m;~P8*K!4 z0ZFiI>Zi2PkyUz~diHB7y()Zd<(bL?Dhn<@{q^^L<@~-4$mL_}__@FWXmHolKV{8X zmtDCkNPNtjG0*go`N(BIsa87)*ry2&G7*|kQC5h&l5AHtZ5%aE5u`I4Cj;AF{i3TJ zcoP!fEU41C8?#|4RP34arDaw7u5&RktJ~QYgl2R(7ZZT|fW!VA{8YQHd(t7WicG+# z(LnD{Opce;bjQ6R$qxFtUgJz5bgkxTAoiq|Uby)>LlXGRQts9Xg1wpWOPu`;5H@|AnueaE;&Yr*p!z}53qVrc-7QXPLS&p48sckL6*~l23wsvl+#eZ@qD?{k}E!>@*~j(GCw3uZe+c6>cFUF(NmvF zC7+C~{t{)_o_?MERiAN})$tgb3cTL4+0ux5*#%N=;LyJ;H-rU?%dzP961Dfy#l=2g z7sV9@3e7L;bw(0rhldkSXDLwUl}hx5Tq#%^zXWR_Rz@Q6=mT7I_Se|Ta?%1L^4NDp zU9)or6R3XU9B02{=iu1H`}AmFc}s^F;7ukNi;7i&ih z)Bjxo@;ow7%fz+n`CL9A&@#?$i4;Th0(zq zq4@P%1npcbS*gTbO0&BD8R^ft-;ju`#KWw9ySA545D}A}9Ns}CKAj7;@tFi&)#MX0 zP?>BsaJb-4lf%)F2=;+n%78RaK%c^)5i9`50Me|Ahl4GHEE$u}8Xyn}nlhj}i8BndXM!{V9@ULn(5BO=r$<`sYbb4v3~;t~tLvr= za%ox-M$LVSxQl5z$uH~snh+g~V|q}Z#dTK2Q8`78(k3U&FYF74k#^;r@~!y%rO(}G_EA+zTka?F#8vv(l>5w`m)5p>zc?}JARmg2a;0vX@8X)$ zxrGwVeI2^a3I#e75dbX2(7D|AHX2wrq@S+utY)mi8fBX&1q}yIO&OsTGH`r?G}-iU zHU*Hj0#KEWC4DbARw|3e#iG>jy*FKP&EG4~32 zmoC^Zo2~LJm+tb7QgYY%8DF{mc~wIt63q`c`uX!V5sy>UWxeE81)SF@eNm%^c75VZ*KB>B;`2 z;ddS|3p!af%~7->3c!l$pDPw;A`&Gk9-}fE0qJzh^_pOfN2QS6w51KeW;$q2Gwc>K z#ui=$hJHLy5Ccv6zghsx1S)re`Nq%I(vb2=FrXH2AtGRbP*dgt3ry$(6*dbBHmpzF z)DwFHCb+zC5sVNNXL5^sPFcLNv>-LCj}*in zB%n`#2xa~aM{dQ&bC}^Iii}(a?`ivB<3!fj+0pGkwBNo3JMsYP=y%-A>orw^cxry` zw9KZ~+_i?Pr}WmHpFW3q)2ZL~;3*u^Zz*gl-tLh|@GTvdJNwA=0|P7Be32N^D_f*juK7AWtCz#4>hE>(_0DNNN*N>a1aA&IDhdw9bkWyB#<|~n11hB zccL`+tIBq9mMF%!i3+ z7PVFGOz=o-eeG5ewfKU|_u7UZRra6A9V$XI{cMyD z6jD%T>j}|h1Ft6zzWU8PYR1716h*Dx5hTjS2M1bZcwGy(MXMlwbkF7HBmQnTJ*tKi<85{MeCN8$Q(z-qr#~Oz!UG+tI~i0b9dl{Z0yvB||xj zSfxDrQSI$sY5BX_?~8CORUpWb6c-C0RKtn(ev$1}t}+)WCwF|-FPf`DGZX;A>ao}8 z=Sm1HyL1Zb9^CP)S7%I4B=R6z$X4V04t(CenRdWvFj$>f{tW5tn$OTY+iH$z=lPtr z8Hs8z(9U~uOipdHt>#->Odj?#Q?Vpj2!j##rSZy$6MhZfhoyg#kxQPix~=gT-67Rc zMJU*dnv;ve*-$zrf0y}tug1L7tTc1QlZk~_Ofx}@Hic3R5ovZU6*mP_5IUbsu`{i( zWd@q@?zuf)s*8!Q8KT9eG|RKUGzP*?L*MCAe%z3Zg-%N_D`O-kGnP%U{MPApJUXQ! z6v^u>OgO2=!ar*yf>Yt8mk!+9#p4YSJoDfdZ?`D-Lm?uLxs_J(rRaWjcjl(l~; zK?+iH{>VLBM7RoSIUI4S@8WhIf6qhQZf^tPol8<4GKO~FDaOszF=U)$eMFfuYdkqW zz+DbI#5nz-fBL#YQYm=$%cDC;(`mGQd(AgAp3TY^G|!J)7Q_n--a2QRRtGJ8K)4{? zp&DP;fJ#t$7p1e0`iG5`SUZ;~VMI#JKc$bHToof&lELh9>6+(v@NK@y&Hh32(2g=( zsSVvd5#}~IYKcssUrw z(x6waKfH!3`oiD<_5Zy0<6z!{&xf)jL%o2P%Lo|7Lh768S0_TN!+x`?g3bM7;bIK{ z6Vm?g+BJTCVDQyJ)=e?_>fj3~(wvuFsXmya5;| z*x|VcAa9N&-KDBKX7XU7%%a%*bg{X~pGvPJ-}~dLNFV;?TIB!)5=)iC)QW?#9M5Y5 zz$*|;0d4KA6yD$OQZgQ-<*qUGEUuZslsAo76}LL=}fX=+YRK2vu_!3iu+bq88_~6K6d23g`7+NXELRGw=j@D~xdDR;< zSpN0LOT*?Y4Kwiy?nVFt`{lej7~*hC>vfK=u+_JN3zv-9agadwoS08RcK&%sH1PV6 z%ii8DEN!`?BSa!z%+aHV0XS@=QCjt-G4=C;tI$J~uAk^!t2A#)+^CG`?VgGcm8PJD z9h3cJL^kJWTc*5x8kyHj(HvdXR``B_E{4}Sw&@Ox#uCibFnTHl7##W;6`Dv`*DQd~ zzt1>$l zy`tr!xYPUpkWSf{f5Sj7i_}-tF$F}i2YMV^5W%qGTd++fR^~PAav?M(Rhe?D4Rhk4 zHzj$00OwBGN+>_2Zdq-K9wJl|`a_LPZF2iA1n!vKw0mMxPE?E?>|H7uedv-Kc3`Tc znERrYG3s7Oo#pO}({__iZ|+swhCx#{SD8=QiDe60DB8|K5d-C-&7B^FbZ;?Y&#M($ zNP_3Qd(pu4q<+gzfPGdS%Zu5$0B^FA6+DYRBgg%sZ>sR_zEnm;BJUd|H}5m9tk*8} zC_fdxX19`qisj~A-_rG9A@!WVvHZZlyfGzJ@APp@I_R9IsL!~3k_7ueI4AQLE3Wlc zsJ2%gb=#nVoiKlk3(I{VD^xFu?on>(6QJU35bBa=XfzR!b_H+p_jZ;uafnByQ$ZFzeFCn{3?&FTXjn(nbO86K)<>eWp)YTN2fr4;#I; zuOdnA*$U}^3y!5y|wZ%gt2Spw?1r~Xs#>Bj<$lV% zOegfQxuQPduw&@N;gU{38I`@@s_{4=;TOt_ihJyWm3kCn_5?TuUw8;s;?(fd+}bD} zSR!4{l&r*?O*VJ_ETm@WXJ(YsE6toKRI1fV8&wE&J`FACU3z^38-{PADv@nR2gSA@ zmNAJ_%^i$9yRo{v+qLC~{I@2mg%vs%mzhz6dhtl@;cB|QY#OF&{<%y6?i>x+MlAdP z!SMKxVdz<^A}37CtcJ<7rLtm5aC`Q=mo}}{tLCH*Xp`pAT@$~J5N)ar{YBC}t_#wB zlImumyV?Xsb{vY|>W4+UU`1DHZWeWT;5Z>iR$1piKQ~KW_7y9eTQawn-6dbFZFl6l zbHiG->gi2dKiqcWY@V}|IitB|q=-+-49|NU`Le1kvnM&LFB^Ro01Z@q<;)xF%I7xO z-d5{+!?gc)RT8;d;?ZPO9xPvV>Q>6_qvS=+D?%1Jfq3HKVUJlZOf-#h-B8Oh@*)wf zp>D75YFjB-bJh_xG>!EE+aSp_bLCUYHr>IiqVf!TnJ5J;iECG?hY&ZGs*@ zMqi^@Gv{UkUbjpVm1gT^CmIz%)EFjBH@8MGdxDJTl@dp%im_D4Ld4O|(=V?dX1LXQ zabx&hE=(>-5wdPx9=)X5(pRBtl-4Ni5NH~T-D9L7$ejA?u6*K(CD=bDz|dU%gf`t3 zQO3ZuZYsH%Fu(%jvnLp<87GR3j?-7JXvC@GpFR5k?!}!!NfITQtWVex=oEq$Qbdv_)@$k~&IuRwktnFF{qbwn&9`6Nb>Uc41%a?M zgG${LZ>@pdbjP58^&MamShIiV3+(fVYy{dbgx)RP)TyehuE7}!6jVYZ%RegiAp?{fle zrZ~A&f3U?pW+7v@D4I(fNcW2BgHx@`=twsqOz=~`E=0rvH0O&X{@H$A%i7trVZ2A_ z0-AHLX$VU&kiqv@&@*~q_hy|-?`nyJ1?Y7xt?`{TNyhP**=B8&I%%g8dVJT|pQ!OT)J~x!odB)G@6&^!F&Xx#i;#~kuQXG?@y9`0` z8jmoU@C*%0W|Oo=J$eg_#%Ba)iUY57W}7z`OL!oVThJ2as~-$ZUM^d+rqr!I^IFjX zWBVC5Xt}pViP5L?6Ps)lU5J|-On4|x5|JRH{|v!INPmIG^6cHduk;ZDTpT-w*`2b=}lq&|5&VzP9gpLxa=Pdj-IB)8~jZ0xqAXJQ<(_Q1Ei` z&6%0u5p%gQxx6o&7S&E2IIwkfqP;HDzf-DTa)fHDUASDWrJ7-OUX|n{3@uxM!@ zW_&@H(PqGBU3px^=npz&)a3oneUBfD$JMVB=SHsCO|dRb7o{ys+C!t{MTlnUx~#vf zb?xF@Q79BkjoXBvQfjTMxl;QQ$B)tPFSYPn%>=h~4pdKK4y21jI}=0Lw_^g0MZ1>0 zMaEQ9al_sGXftG#+bw$q{AO5i7R1BwHm9v<4_%_U+g77UVKY3f)!YDfnbb-^Sf=9X zzUTJMO~iU+Qp!wX1*0>fkuR76^az-TxMX^$BA58{Kh%H&A7|P+L|>&H(ZW!uzBj$C z!e7~-%Tr?&eZCc;mcswvsPxK}{4kIt`JFHVrJ!^ByWpEmM2C~*PgS#&h!5i+1eBY&9lSe`3@5A=D2})4dQ=Lbi7ELpiQ@aGf`O>dG~-{rIee z9&s}0(W>Ca(zF2gRl|+DEbGjMZCmj6<=#PJ)7>Vh$6hE6ad&nj>*K!(9`EXsj{E;E(NN#n zqq}mP(>xZHN;%~eYdXK62QEvGuyRNb#S zGVo+VAqX@L`QWZD3X+OWkpnnSEM~p>rxKihGE`|+4RwpLb$8_IQ< zXVLJ&lFU1%8B25DCl6kvrxKufD}x$0RaH-&sQW^h_|UfME3G87B~QCKWo*@@Dv{b_ zK&puaMu`OVV>T3LX9e_4RexXEelcc*rgptnyEP4o5c4fo4V&CB9gi5nAQvfLMDcsQ z^VG9qF&i0{BT;b8BYvnDRc3XEhGa-0g&L$J zwlZr`49qW!tK8Hd13py~UzBx+xJKWsC_4{hGpMNf*5q8{KjbHZJNA z^jbTY%}}r_Ptz%g(^#edwhcZ=ca_8*&Y? zl{cCt)2II&xO<)-uML|M;dle8ZJ`~f2E8$F(2}$CX@l``6R_kU5=z#}+)tXXCsrYe znIg9musw++6$%Z}mo$XJ_)Al|E9#NL$|hRc+nIxrC#2?vrCE*+;Lu*%7Pkduz6Aoz z=6?VG_kH4)EQP{&Cn9sBZ{MzDvB&+fAEV#BeS0nl=WFQ5$W%&MJ7#9;mhXj**J`Ir zR+6|Jyh86Q(e`S^+yNbNO|Dl=uOgcpW%Vze*S5RgyIE$L{fzW@ccMx4@;YnlkxA?5 zaW003$Fc~VWK36SZSMTIvt1ql$(QxQ$NOCkX3yfdDS|@b>U(Um*1NaC9boQ^vC3-J zexu%o-s!J9#DP10tv9j7EqX!0@7UK^!6&TF4s>Fljo2K6S5MV0n9Cm|0Q3e&Q!rA= znpX9Z$)8+E81nn+%5I`6XaO5-DT|>j8V0%P3hEr&E5R&YWX(0Rh&Q}B338(XS`fzLR;O0^i zd>Hn<8c&)sFK*C4k~U4@vH;Ce=+&!2e5nwaToqMrp`;65!)&i}-NFU5JrG-atd}08 zK?AM@KeF)*dP-jqQZ@nvt^QL%gXO>D3BQc`kD#^uZ_*#iOk;S?;n2L=z$7UxKT4FBS~l*jqV5r3fL zc?yV&`?|@ewX^2-Wh-^gXstuOJjO5YEOQBWd8of5@oLxDN$2purs%J=pL_ArjuQT~ z`pGQWzw#ySrGw631ydqhJG9;XUw&X4AwKL~`rM8aD$d$;T{udabsN{W56yK?!3~Mk z4%MMZK8T74XzxsGaW`k;61Y+_7WOR4s*$=FT3yC`ppYc2Lt3S*wviCb!H35qsum>>o?g+x^38-2Cux#N_m_E3sN z0tqF7xNdRLU5MqF$v(gd`g-)XXqjy=ke8ct%L6}x@&+Ke05ej2PWVuP&-WV7*Xz-^YdpaeNVp4 zS347URKFp(y4dzcf?Euw`K@p14Q!Q&zAE|}u&1=ZO9lazgiD9wRd%-AyvB^#t4>)o zn zTIh5Ujl*cs#>u;pQp2VJM{vf&6*oV2Nj_6aiBDkj?Gq;%?$-RYrP1murR10)yKlB$jpRoq* zU7O+1_k{A7X`)3)%S6uynj4a-7SL)p zY{A_GL;yC~rxz{!hK~Zb)WIvKeOgsCpI)x#cu%$6yq%wB#r)V&9!U5b6c7uI!s=B! zB1wDqDUsYUg#?XSz_9olF7?xcD{h2wDDc&ny!|Y+GD2sBK(aaW{CO3T&3Tvuj8CNjN6N2 zc^<8pBeum+YM(Y_a(^QMr^u1Bg5DHL?aMT55*qSP76$I$#wd9XhZgTn_04@GZH^3E znglJ&eDjmkh${UN9h6h?id^^6oQ?kIhlxNE{|n1N3fR(~3Up*`2 zijvce&z>hx^xV344M)^U?$&HBi@N=CsB!yR$aWt@D4j$@85l>8CgVft*s;SQ5ux&v zuRW5-qk1%jf{J!1qa-^6yn6Hp>aAVR%!xZca8VP7<010#C z&pr(kf!0j6UhAS}@7lX}z714Y-k-Mr2U6J$%r9TLNgk@iro>GrLVqrvwAd_Anl0%1 zNXlv{{r)9TfBC(>^h9tn+sIz+UU!XPOV+D_OXveoVLr~j@2jP1&!}hW_$mEMQ~cA} zyb|tYM@Csk%p{W)s+AS^SYU_@HzktNfMc>tk=jufPq`bxkAWgW)u9_gl_#s{wq6h} z>tG`AhC9kff1(D{|A5GBWz>?bPhM<^gF2Z}8KFMxG&N-#7Wf)HTQ?+ny{83(w0{iY zX}{%0@LVcF^bQm!$DPJOmJ9`JZ{7m9kmpTCW4yrK5Wa+krveuUd*Pv0edJrHe_c_J+3K;Y0fGo2K7-^3KpC?_WFK2zB=YrOQX#|1ZRY}N$ zsjg3wbQaq1zOBrX2Esqh)oYCB=NAGx(#X}&Tlw5RR8wig^q~--1elwg97Q}g_Zmel z?@kHWkas)hZA1u-uXWbPdM8_271IRIjYHLUr-uPBp=?(Ras7yfm^#HYOSK& z`wvMb^~2LMmRw~tZiUa+5rruoQg&l_>o4?H(nG{Q-Ana{or#-gdml%+`dImrvbG{( z7p&tb<2KF1iyEl$<3+|T(cr$3H{GD2`gSx^hn7h3?N z-7f#2g>parXHTO6Xp+A#C2Zuc{Zdc36GglYx@H|9PCaBM{&in*V!%HPSi-P^+!JO5 zI@rugFRTlbeLpC5i#EQCqt8&7BKWgRe%EPME#GG`?dVxT9A|p(!G9fnHgQW#ss8N_Q1c&3xd57=V@14Ul( z;Oq|aNiyHKuw+(mm2ptbABVYXT46HV*GPgdjvGBFxMN#vS0!oI8@L~%w_{iUf@6pe z!J}wU#&NgP={AWH8DsoS@;|-{eIIF4Xopg5(CA$r`Op>xj-ym(=xp)QE=7Xv{$V{4qbf+kT65`SQT( z!ZyvE*xJEVow#eKj@8VD4<6E)84uEj`&>;30OfqZbRZDZHBUS=J|IdC=Y78387%)% z9dc1B&9C;GL0lCl^(lD;dekR|9TQ7r*scadjrLb$X}myZdUYo;Torx0UU9+a&q+K6 zK4o6kXer21DjvD?6l{8}e?ow4KMQBv`LY4j_lk?k1Ir+oK{PaH?B{SH*qzj};=~S$xWpk*YrTFKJ~fRkm`kA6J*@ z(N}Xe3Y2Hsg` zd_4%nK)XGK!B0X5uzJQ&ykzsh$u(ATY$O1^q0w5^ggB79gS0qa&ySdKa40%KHcB;6 zSuzO;!>CpsnY9ilN0f=q%y4Dq;hn8qwyJ1qlNKKx4x-X>n%%9B&MK?4XR z6VrUXNWt|*BRA29)zaX!+%fR}Xm1 zh)0bC`jGnm?+!;tk`SQRu6~VKx=N|OR5wj=Uc%_QBZ4r2r{vhfwQ+~O1RC?#%j#l_ zFq%tNZ*=in4T>4nmTeIZUgv8d7i+Y-Eo94Z+TEXj|F2#QO7z`i_A{c#-IYcf6OTsE zROZjR+n1d=Z%+j1JTn zd+6vm8?`#Qp7VM|4Fn(8W8II^OkLUcMnV0%8i zr-c?L`(fwaopm_}=js0UIS}xkC!hfcsZ1Uc`D4(y%EXaKXp!_}&7Sgy>)}~Pk7k*v z0R*+iSy#a$v~R zeX^24%(kxlnZBzNfrHfi>tqOoyp%v43|w(75S}?G)apg?N;OE`O0+b$p?Yc&Fa4;>M((f(+qN5a0fa6{?2lCvuLHUtJ~ zs?$>|(7(8KG&DIi>SSt=D-4F6OKZ8(PI2i%r5OSRluhu66AmjYKYItpG80XMn@&o9 zR`GQZ{5deuBqL;2oG;ZZDUr_&L2EFS#)4iOjE8~wMjVvio6QBl+}v)l0*m+ix|BR6 zq7j@*t-zf3jCOGVB%GV-9-qnRuVe{8>Sv@<-AIjL3V*mP=gMK7dWVl_LqBz>zeAM?E0)b*m z(-tW@b|C-yqZl(%hEkVNw2uUR%ev%$PwfoW32O$$RZzsii+!`7Q&yF){S3^1cz<&M zQOa^}ud$yq9;5$y=a4dqMi8Wo()uUXucO%AZcab&9@l#!UG*^*LMtD{)wQJ!^~{{|qje>0#VA_7t-GV0Vt=7IO_^w2S|1KGCn=&7 zIiMqlKFliD13Y7lJK7x7ntg0O;-~v1`zg0pU=VC&Sr_guH7d{#*$<^ee(Eg@iS`F% zHA>;eTJ<4O1GTx+rl($J0Z@RWFJ@}K3xQP1SdkK<1Xw00W+4cO!<}9e@|b5YYCH+E zFWSfJrGrx^O4gG#;Z|M={+0UQpTC}7#2Ib8d!Ua7GQO-kqNNQmX*UEU0pJe@7AE4U zwf@t!j*X40k61-dQ|KSSc*Zpj9>=l0*@|=`jumLC5r}r@uU|vj7K7zem7BeOK_t37 zhCmC^0leiNW{O-pQ_NwEDVnA>L($P+o!;NhiVSBkC^Ts;Yr+#e1qvfIbcC$AnegCRn?NkwemQ9q{hZ80)DRKKV55>n@+ zrF_6xec$!x3-5M?t7hpcw?AKqOMFRL_1?t$qmqSty(Mj6DiAf?M7yNXV2p=OfuA`f zBa>sjholVH6rcqddf`ip%Fh>sbg|fg9}8rHx@*{h-8b_G>|28~r~`VU8QhR8o~FUQ zVm$X6d{aD^e%QJ#Rz-f)Y+bL?@#<8df815HKiz1(<-p~CrfcD+F|np^Vcxs=+ty|2{Ww#AoH6&% zo#cyzwgikJ)APFGIg@CG*hvi-ht@)l>k0=EIZLZ=Unl@u0cII6x44LJA^Z!4lKC?+ z9iBtCzQH?K4wgx1B&ErK=cc(pgvCHGS8NR*-4R`eCMk0^@ZhL4ck!fIkTYX0{Nqgm zXA54u6v#2s$LYCGvvG4HO>^;rGg?keO=~o~A8voFukYHJ1yE)-pw)>!Y}+;oIY8agmiMNa9*?C0;5E;h zHZt=0bU-%>p5aW6&N2xd_SY96bo}-0C)BUNVo1v5@6@~jh<6gp=2vF&@wdr}H$BYT z{4PCWcnu{5WIqkMf5GmJVYAB1Ad)%YW&d!Hr;EKvkJ70OOUUK-T=0;^+mHL5gr0C3 zEfR5KgQKbmo0CAPN#e)o^I~h<*%Y~*smuj4Wl)?JMmXI8iCS${OeonAC~;6QHNP2d z87I7@!9)1R!d8j3ifO>Ls+-yplcA1kmC*3XzXVu6ap`AXI@6oLTU$`DRye7g8L|tZ zpEjfb+C53hi6{uQV+PGfmYNmYK&cfMz2Hn@A#As71>D9s->gk`+WGpOc2;8bao>Iw z+|m*+q}t6T$4O})h=stm(t^*S)}vJOojv*?LbHPePzF;5I;L%%b*y%a&;$ig1fR%r z&(EdrJEy-Frq5agd~+-oM}-f|I^f1|NcM`aXW8ji6?K547g`8XK4#|3K%L?MWfbCz zu0Te^JT~LavfwTq1(Ui=feqFWFM%nOSdLj|`ofd%rjvvjgu(Vy^JZUHZQ6_h6WNlg9F`pn0bGzs>?3HLw0ZOK&|M5DU zPKimPl{Zeo*d(cX7TUPF^a~>+90YH4G8YBWFps2b{&?jK$gEYWx3(D1 z!<21adU``7ytCf#r&HikiojIc~8C+D%CNYW3!UMh+0Xdsi zJa%p$1_QS`eLF%c*M|;d-cycTNT3ng2n@+=H5Bb2YKy3*W@TT9jMnMqPRxN}#5li# ze0*p1fWUan)K^A~Y4FG;5kt>L0VD19O>3u&F_-A{u@MHIcSe0TnJmI^0V)0=rO?PJ0vAVOUPhak5s4~M34*5kF z25O02RuL8fQ>{_BoGq=8f#?NIsMkGNodk7Ylh7DoD8 zzPfI@YFNx}*sLL!U@enFT-YvoYpfdnBm?&Bf@OHevw%+U zNRBWjHA7s0U^svMzgEe2yb+DSJl{eE#<^>v`hffK8eg-Ib!p$35ZH= z5}7G;Zk%*q^70w$Uk`XiORbbdlm;NByg~_?BxhNeLBCc$A7><$B}~vTOe5~&dmARs zotTzJbPr_fT)?GJloLIi(i>qk;>rz=9}hSpoIKo}ii>mnOkQ42-`w&=W1Po!xvcF- zEnhzAm-46a){EHM_yRk8D~DsL$RUfV1i!Yw-s%fDz8_C7(k|$ygu(YpZpJvgCa5gz z5rLK^>vQvTkX<$?3u_0KNH*~diAHfFDBFo!mU)+qkEVP3!7wP3Uf{|L*1y4G*7)n! zqpZcO4g-UdfaDhx0NmOOot^!(ktSw_&U!;}Nr}%A5Eb1#&YUEYt0*XFT+&5E=|j=< z9|0W|t=$~l^XX$>=y>)o!GlGDE;{5K{rqWO_{J-W&Yzw!e;C)M$@9{JN@+AeU~GqY z5Kiw*B<7HqHp9|Xm#W1QE}fP?(CUxm4>Si|42@W%F=%{!XE;1D$fP_A?m$ZdjhZhO z$MvEw3*)8HHSKT#$bZ+I%5UrFk#v%-aEB0KAZqEQbl_q|krJE>MX7oAwZ0-PRqgo|BCn>&`IF=Y?=7?)5<=Q#D7yDqGNhr5l|ces8J$>Q}~C`goaq;?B(t0HPdZ@otlM-AqfX#@VUglq#y zWsHU;X<;Tgvt)_3&m3ev^ZX7iX$`k*O%m?D+_2dep;STdlq9yCR!B#D=dR@7LJ z85N`5m3X>xbXYH-LD6v6GPDl}URyDKQhVzb^W8M3^|hoU-b4nq-D5+^lon2;PL zp(ocvSOQQmHb;Zou95p}Tj@NO8%~3BV^2n9QToa)l4ofo^B7W2=o7O2Zy7hzS9+Qa zUv#>;B0uVSJW_+F zhC<5xXSd1N+X}5uO%?u&Sz?xr+3NE3!%pTXIOg(K;@F{1e<)9X;eFV@x8p{La*u76dWsCAC0 z;3<~x07XE$zic`7(5?15A?1C^k-R-y@)9btnLDSgvH^s3d$6>z1M4mtq?T|Iz2YM3 zA?o4=EdIQF9Ci+?4{lBwn@bE6?KU%Y0AxOc_BM={1iR09FGv=mecTfslJU`zg93YT zOo1Jo@g$P+4GQO+;4Q?&^kJcoTaNzub94*cZc~hIGLFQb;6R~&lI|MOw~CDqzYY(N zjCe>+aKWO9$K$o$5FXMp@zCQ4CIsQ>3o`==r}2dIkaDmk(QT?&E&SMTv9|S&6XJknCMcy%W2@rdP%wEgdul!cz zeevkyGTT7sO3FwDl~dss9`+PIA%681n@s6mWE&6(nC5c8(lsyV9gs(PP7hc92rczs z1*EYX;^fJiOiBZui#@5-C{m?XGQ-G^>`gnqI*TpO>_G@HJQ>KO2~5KWF-$y0DAG#q zt@IR34uMfZFui753z0sPh|B0G^vM_P~}qobEq zrQ0l5Oo}5#*R0Y-wylJR92l8TH7-l~!I80%rumsuY;$h{jKzA1WRep%|$Mtgz z>Xr+=pZTauYs&7%qXV9JSn}5Q%GN$Inb@Zcg!Jn~;z5y>%z8 z^3vmGU7;TFwL<%I6im0bLCFC%Q-^5POQUw?oOW(4%3o!?IS^&_RtF+&ldlJfLJ~Uf zM+45QzIfJS^;%d8uD;1{8XM`_dH&`30P?~}5KCuNoE&~*P6xuc7wzHzhfi8dI^1I1 zK?i^(IYS9uox^YP70QEYqMHOIy;UmhPlW)g916w1eH_QvJjhlsxs zzRRIMb@u&1a;aLGnikCh(OuI)>sTNZU)6T+O%J?}F;*Owza|+_T<_`~#Wq-@lQQe; zoozSdrLkLV(vK&*9zm(eQ8rS$3sVd2QGM&{l&w>T>}7wI?C(l~^;=Qa)VPBkGn3IpP+HR#54sm{HY` z+mRkD9%1=qq|fB0SeqliDuv(YXIAV~ZgKgK%|}d^D44=pDbsI+P4mHNj^!aETG1E; z%18w+gU}@LiOGOh`t`J+uUxQjskjx;D#*6=jSCkq50sTIXTH*TAUTuoOfr{&8gQp5 z(IZ+dDQS+uxbwB$YU{MpYSgV6Js%ppFk+MQ@*7}oqcGrMU7Tw&lSwJMSnWmIIA)e^ zM6u4dyCpc1LsKr^Z`u`$#G4rQPG{dIe`MWotu39|N|QZdx{AG7JZ#+T$Dj;p*7UX{56pUxSdX5*+lmX{xiD172Y)8r^qOtsfs`JakDoOQx94|Zfum+8Ls zezZtV@&Kz_v2H}f%*thGFWQJGGO015Xk}l@lu>S0J&{A?_VALZ`AGj98-GQO?`Ion zey1g>LZ#y|HU7rnV|vAv3w8~GK4I%wfbk`UB}`S4+3I45lSh*7q z+hO`l8Q2kJcgc&M^(|;weL5bf!FXvPPq_skm5O+LD_)Dkv9d#P0VRZg1LnA0ds|x@ z9@udrnhD%^KuibLb#T>`9o55XyXu1r3*6Q%0o~}MTRq8ti@^1h*ru{v4Dn@&i)wLO z{w41mvtC!Fhm;x_C*nwI(|N*U>hvW_IEolaZFrT!HA2U&7A(LOnqvi2eC;=E(YKM^1`El#k zQ}QEbC`U9$-j_)}w5QbIh2(D4+Jr@t1`hn$ssHzl@?M0Sl7Qxy%a@DVJVYcuZt+M* zTgMhni6_ZJ)FzV0xF>J;a#d{z1%Moi#u59?PRq~TzJGU00Y8ZnP-B1t17 zR+L{Za&t*>4R9ORsqnewx*$Ff1j%AY>`r=>#l14Jah6z<{Y3dmuGV3S_LkZwNdFL4 zgH)oe?3}!rpC6S)$#jo=`r1deGnOa~Z%=e`N^B385_1APJ3fuNIMJ8rg!Roe5xQJDC_U?_s{tY_J-Nuwi)+f zWY`BH3AvFA+bwfZXCvY)F-@=*oP4jXFR69SX!cT+vC}QbE^8!5_)9F^g)w0jJz=Z- zj9E~}LB=d`lqDe%*8d7mP6ZWuc1||eUZutZKJf0wtU>8^+)9T=@YB7`DX_^3FP)i+ z-l}ZOlBq&7M@<==uP0j=kQyv*To%6Pj9eXS-qE8CZ7~IF59R2j!o&fVtm}T)n)zyOF+NOMiR^UwBUR5fNa=fSkCVa9152N(|@>YDi4> zO%JI&l0c6qkRajwR%$ zO>Wq5=AjE(0Ms-6Kt3n-O}y}A4gOiWEJ6fSvzK+T!b$J6YU+fqO93Djd_VvMQB)SN#!#r_D+d_kI&~iIvSZzS(4M_ivYX2bq40%5HH_M* z$^tksg4Srrsj8}+r(w65Ms@aBOk-Q2Zcf*zcyvzRM4MRH#VQd_I0ORy@W$NX!*e$t z0v3rCeE9YlhRre!e~<-Idp>cWJ{Hro9peUl!p4jv$vgDAsPKfCX;7=1yl zVD}F<8`K3jl<0sMOc_Wlt(rF{w;X`k) zw9awDr~6u`W$5Pfn!R+azh&bYS84v0w}D z2dB>*Lf_-4s)9MGaRN8iK=~Q5i-NDXC$tjK?G_&6p5gi(t6M!~9vq3pNGo2^m%7E? z>R~VSM}-qMjC$2P@HQ!V(6)!=L`dX!M$6Ch;}dq}`uZ|%M!hK|!({mL?*qB+E}bdi z2o%QKl~6Wb!?$t?jpGD+s%ZDfJc>-pKeI__E~mGcjsvS!7Y zusJ3)F4{W)=5srbLX5AK{q_nHnrrs;8QkXe^_70lKB#Ib&#-wSRLkR?ylTBoRU3f< z>157=O}yQ)t+ZSJghcUYG!J_kE8*RpAE}H2p%*%;JcBuLsRFkF{z1=w6aoc*p%r%r z2~2&v#X&v7qc#&8uiKzycKF>vbrF;+Rr+85ANEn+GiKgDpXB0|8&bDimk2NgQpNxn ze+{HkULf-<_n7Ne(RYR1SE3so6@q`V?lR(FK?xt_cBx0HJUI&wlgc!1SUaIVy9165W~)bEVdWK?t&E>anro9=REA^l2S{WD}o3I-yMc) zHONyJ~x~)-!6B6-+T3?r`y=Z8V zO!akq*TxVy`3(ue*5q20roz;H@kvO+I>w7{OMSbH3d~_IE!AtI^LSQqFvJ4Fa>~ws zOhb@g;DiViL=ZM;Cg{79Q>AfzaNnr%J(?J}els|}5TWs2c#c!wp<}+N)i_mc5wZ7W zemAhVwjT7ER#jTZI`nqNuM6Z`ZRtLRzY~Bz(+$xG;BXs#^j`+y`4DGI214ERq58vL z3MK1bq-Q<%Noag7-KE5Z^8Qv1UNPj8x-bbMdy|$ohJ$T}bI>`+59*tyv-HtI;PvcI zo|H+!6L5#jX?qG?N~|F25cWDvxT>YndE_OD#dU_~)dm2+`bXvj&Hq-`fuRDm3+B=R zYXWOLZz&qidpsRa@kdJ6rJ;C3PHHnP%c>iy@9_{QpEUqGU2?+IsT<#j` zWPWZHu#qxyaxzb1yEcMbmQ;b((h5=-535UK%USd1ii`NKG-F+nKC~31jRuTxdElq! zfocYDIvNB=U9Vcu=-9|45-b$pGVH3D>%Bu-UOz|o_*Q1(?DprNv9bjF7brsO;7Mik{3{fR zIjt7%It@V#4hzHeobL+%ymqLi)X+54QbM;#AlG{5(X)B%eE)bGzOJ0squW0&_+)V&)k&ZlVcwHls)yDF-7GhRwz{SlA71SeGBHRa#K0Baw`(tc>suBaw4;>+a^8 zyE`uH>D?LzyZSD4ir1++>Pr?$R3{gKHkcZf%5688(jxLY?;7mlzHc#ftUNg=wW9_cFMZljE zbDsz__PRp@cT8%1DH*Z(;yfsZo>_26cjDdiSBqYf{YXrVEem$b+i-;W#F0P&cizO% zpK!&@xt&$|OSqT7p*}I|w}A1)Ov}EhX5s`eaEZ{)j+Yxf)L-k2@t+|J2|508##_3& z!N#qw`E-OWV_Xf@2|(3x@m;c#;6p)5w6Ac@P+@O;9(k#3PTuN~dk;p2^C~m5M$q`n zcuap(cA~Vz<#{E6V7!wZG^fW|(pzO%7JafdOZ-X&%c+Es63hSqUL!oo zoyiE#N#9>D?yfR3EkLnsvow~=`(VoKP~trS=1V3$E-C5F)tp#%Osa^*X0dPC3!RHX zM_t~ojTX`?0`iOI*n&`bxX?+CZmCva=4&l}Q;fxA(Craq{Q}ryRkxQe+Goa>C*2@1 zPKy2YtuRm_^Z*E<&aZ-pNR{oVT}WoI5}prRv|7S=%N^py1zaw|Ad%pJy(^+zUlueI zVwk2+cCQ-$f{KzOyRP=Jh{bjxf^5tLEYx^B>>5N9cu7tIEk+Z9>}4!3iCk@h-qU2X zP+3&RXfPER%PaAAh7A(j2^#CyZFwKZ=7^+l2SZ#n&oRS1XbWI3xcA+g0SYCJwuqw z0lq`Ao}SV699L>VoU*kH+D~c2?VpULl4)!(2N*|mV?75{qY12aHJv=!gz<&?Cryez zBL$AD4emjwM2Hrm!{oMw5TYsQZG$4moADV~ArKBN>X*)(VZKrxm8ycdnP08+k$ovU z%{w*|#qZFcvM7#@Z#veL{Bc8G{rSh0?Wy~%+qLPfK|PLo`5I5}2V%+zg=B<&_{zoG z+xxbS*Y0R~mu@dgewfFq#iV*u=qyTtrb;6+#jV5h5NQkH|5|=uqI+Yzj2>NY2bN+| zI`nor>!afKKV?4&bXr~3xZl;F-)GgTO=}M778E9qdU~I6vmfOp!&O69Tv^`QyJd6r zwuU!pcB145xvW~3WbX(X6cL|PsTNk|tWnHEjvORy1jLMMz-bKKceKX81rj6k=C3;s z&G^iV$q6NS%SRurI6yTzd2uPUsH}YAjI2)G=RN(j#_Yx2Le_!BUR?gEQ~5Yu2LkK$ zs$H5td%U1>SNXN_(p!Hm?71sf4;Z9z*(qK!)%f52$1TXr8%s-|6fkEriA>VG?j}$9 zvQtpJWbNProyDFlZL$@B1;;-3xZU%Bhi>e68_H36S>?2j0Ak@B;)!{tLlRM%2%FBw z`auBC8Ivgpn2$os>qKBYV3LUJnZef>v$3-91?j*3H=fA{k-H^kBBfc07Lyf?`#!dk z+0dv*UEEZC>R@OSr8JmDa98lcwx9A-gh3Sj zPVeG{tq5mo-YMS6?BXV>ie#Ap47xQ7xHPSQA2fbzEiy~0qEPxGWkKaZ_zYE#=I?FR%$ z`X}qka2xh9=8he`O2Zg!>S6}k_RZB{TkkUOvE@H&OK|}lr?Mf8h(Ik~SvfcNDxH>Z zFz|tqX~j*_Y~(%l-@5#^wC$?DrIPl(DCsw6sl2~mtKY|&#{^g9*rTM=E-w3x3XBeL z&D$R6Yov?=pRNn;BM+?e`1rwNT?Rnl`2+5kl8tc#i*K597G11%OOC*4UDHDqD;=6k zHr5L*?Jp-&qRZ%eR;uAfBX9-Argcvy;pJx@^m>V@b@JeJlB#%ROq4E)sCM3S+)ZZh z(Vsvs(E-}a6UbJ? zi)t=*-PZ9{NTKsE!OCsNmDboQGZLu0htOgNbTfdX+Q}&4&m=}8vBXe=XnIucAv-Yc~5wEt#<(A_qRo#V9!r3PQ(T_+p zvDb$fg~Kxb)%*&vb!|;U&7}tCp>S;~S<9`fi_$p`0m5Iqo$}%pN)cPc^YgkcIkeX% z^WiLVfJnG$--9^Gg`n?Y!p+vm-x-%%zfK;QZnOS8jze;IOttTF`ARb4c4HV6{^UM* z%?bRR?$#0HN*;nEb>pN5w>oZFlNOzreHv`^dcxDLwCP@1JD#@Wv3j)Xvlr8etTDh~ zH+qA1FPfNN=bV$U$_{&w&l^1_REHp7O4+=1b4=r+>{F zJz}v137f{^?qY}leL_mwIf;h)#KP2$@ky@pJwsMfjkzVxOw~oop1wSB86Z#E4XT z@RsOP5gsq4QI%Q#rAz&e71cMl|C^R(y%bQy;I z=SraX>8v=nGuK(Qwce=wMqWCe%!=cD?vBcuIAC&p;8EwnXh!KY)$5|VY9g~bYoanc zYopFCEbk`%)_U7iNk+F+dH6k@OPRtu!fW|{B~$mW6rG`^P9mMg|(`OwEA(}UJ(8eEa{%8cMe z%`O7PK5(|??Uy0VT|B4)+wy5mxdFml#Mz~8&TD!I`8A0Vy9 z_LYqv+(tyYkaA?dME-0IVQF zq6on(SOc)SW|R7tuYcQIk^a?H%$GdpFj7aqHr3b^DfUK#a1 z1%xQI+DKBV)IxZTwM^89h-xhu@a^wm+Hf4=b(#WY-J3M zntBML_NYog>eV&+tKxaMLl*~)Q9x2sae`0zr?5OP9ponQ9Z5$f0xfVrUsEr;ZEmLZ zzu3Y9W2TT=H9Pe@c?1a<8hSkmdIs)AmE+0`hl$i@S+5i(+8GNE>~;xS&2k6 z&H+5_A3=)xrPCLtkWR;}m6~bAM3wdqP9%TAHz4izE`}h|E6c!V97&vKp~gD3BR}D| zq)>H7mlts>H9RPj8PD3TEl9gcM4ub4xZqVWCTHxs&b}jAxdIp?eZ+&1i3cr|bE6eJ zNt(*JjbP4uHo}2$*i)qYnsq_zoNa9ui${ZSJP_@f-1>9)PibQ?0?M|6b-x(+1)Y?f zW*)*dZzB(^lAMws+SM-aZ(W6Kt~@AzN$b^?E6^ZY6htkSvC|S{q45O2aUJTNyWuGr z%RE(3ad~f1UNkvN9Gem&2`a(A@g-jV=Jt;wRv&hR94als=IV3Vc`+hRq#?sJ#t86S zRV2}$%8OgA%)m{3f!~o&zJGE8J(=}OEs+NbiN829N#(8n-Yby^$|$iNS!8W!ucpP2 zh@1sXVW7MuRhd+mt_t>)L-!~K4+Os2<%%7S9VZ}2CqF1Ij&~sytX# zm#$Hiq{;({!UaqYDMn3;hhD2bhQhpsaK+vjh3_!~%tE-2YOpH34hR`f@__ApPq7XR z6fA=70*d{S?l8&Uu&>Iw0?@tlh%6j+?umfI=!E>h!V0uVbN&)Fz23yK*~(I-)#@mv zhx7G~E2PjyyG+L)KSpRHeo7bg^1U$+^^}&D0vrpJw4o4iDNiEJElS7|{c#Wtn*zy$ zH^+50mDecSgrdLqtL*>omLX6;f$9i88pDAxlnMZ(CKMSbj&n1u*@uQ$EbBR0gBN_i za~iADLC8Zzc5udg%(^8Mn6m^kxHlhvlwT@%L+j=^&k8)FB8(p!Cn86|wejcDAqU;U zqr?!T=T`OWv#H>7z$QF4L@jNekHMRviw=Qwu5_My=y5gvw<2x#jIX>(>)h;pU;HRu z4!v#dCsv@do11eI-U8dSM)y7v4}B_g)>g?C(}x2VBCw{Q%=c~lx3{eZ@BI9z)fV)r zId5^Oxu?3(`Fp{XZ>*3Z3_K2^e_eM6zd&IQ@FQW2#Ob+N*I9jO!J?GJd?V6w@6ufM z2J(rQNelv%U*DODS1a4gBJGim|J+X8o`Nu!e3$2^Ij1=2*1ZZY#d&6sq__z0ZtVVZ z%b@`1Vwk_qejRWsHAN!<@&$7W%XUuQIX=*1$>iv>QAgDw>wv?W#}9!x{`}C2k$JN= zCaTH|y)81ceo_0D%K(8}^kLz-mYD0%z9}`;ALHZM>0euyk$Uf6X&&!%s^#-yDBrCf z8c(E+J?KL(`pMv&4DAlE8BjDo3=cWxRLd*^?lAzOuhp#56oxs`%_8+?z2M1E?yRO= zQ@i!sAJm+GC?7C(H2ZVUN(XadwV7^Fw|nXA{04o^3?sonr2X>u?#Yj!@t+x(RoTJ& z6TPNhzMN7k7=bS~_a_Pxq?eExi;EG+OK7L}E$!b%_;Z0ZlUV+=-j-PWd00{RGlh;?}k=%CeTjT3gH8S}klO z-cE{TlvhYs2G32%Ul`E}R@0~Cc;<7H^_E#ihG;W_N+Zn02X1Gb;|^{|d`gISN$vPb6iA3F7=ul4nrMeB6Y z*XQm7VkWpe4VXpfU+eMFaM3VIbb24aSPZAFLbS5=tS(aa?fUf!E=9uP#EzhpbuBPY zQ$oYO7;OpS+ttUSoS^aIlk6G?U3Qcf-(;O&w|~pSomd(FQ2*eZ;`*Cg4Ht~+R_;U7 zG*1wbjFGjFzxOaEddCv@3C?)J?>!L=pYD~CkOjz=7SenIVc z)*kS@Lr_avssNX67ObD=zEWqrym-PZ&h#5;d>goL@yeXy@sc>Kw{M&maZ0mb1Dq7= z{6`er;eHH;iOH33AW#bDI1sRT4|Q>Z>!P*U!U)Xz*6@&^wfdQ-jg6m~)r>vHwx1K5 zRNTV1ZZdGK61l%&K^-sQMq3SCD{x-6wMMlUo5U!}^Zmj<$*ePHX94rG_1O*t>`^JS z0mH<^inR_zOl>sxm`6LmKR7YhThXi3RMB&PllwK#Z)ue{h&rb({Q!uxKDj+GFHFA&Z ze4l{Gq>7VX%s=>geYaciqQHSuR|i%1y&m=(u>|Z?eHwv{KTOxa_W2G~&0f2}jLm%* zObOC9Xt+4r4eny%jmM5f+OPs{yf1`J0nyn(g$@MlHp=4b`?ixdO=}c9>CAOGjc+w6 zKXIuEBgQZ>Id!8!F3N3K0v4%h$g1*YXU0)~8k4uWS8wtDXRScS>lk&cJHrXdZxaa*E0_iv+lS{OF)}dP)V5I@OJP>2nDX zo-+~l_juI0*DOc3Ae~K1WW1WNb{8dL?XhpZgMSCsd;;M7t=eohrFscoVM9kddRA<> z4j_DA^}`RQ{cYf{w?(O1QEZ&*yN*Z1H?2wk-`wgXYdgN!d(4dHe{W=Gps5=uM& zs6F0!cNRdrQoq~f{&Bh)TmuqoOE7yfbaw4920bEo4KRPiPTm)k1NFRe4X;G*ZrTQe zN?$c1TWqgUorX6^!WMtQ*YhxV8~87K$A$rMu#mwxJ~l?O zz78iaDhNkh@=@Di*Caawo@j|?6aYm+*ZilMLlU}{gtskV88Cs}0V(j0gL#x&Xv&e1 z_7lIvR_c`sNHU&qLy8%+cu}=b!lm%&IhqnaCVFS#fUS=zl`Ct>yo4vk6u-(>U!;CX z`L&M0P-kEF5JOLUV)5e6%$A9xs$tc)^R`aO$RP00^a`i@enBS=l`jHG+2!qwpKr36 z_39rYrwrQMtQsmXcLJxux%04r>yAqrqfbnDi~EUbF~ChKf6IV++?TO?nIM~O&1Fiu zAuLZP_NZDiPKs>~!Vd=GI;gac+@dN+$6(;}cwKYSwj*XlT$m930rI*Pqr^r@f}Kcr z^X**{tEvE!Nela;kw3UMBNfPkRf#U~HFq`1uFg_FH~ZEXkPoipFdUIOy)&u5ZW94; zCOIbOR&{W&9kirDMstu9n~WP(V>?NGyCGbU7_L=z!W*>ZeW-*1VuHU9nR+_S&CWS_ z9^4@yQrXnl*Ur9^?vvj9smcmYKq-kZ-jI@VOCAy`-Pzor;FIKC~AnIxkg#JEFRE_du zH#B0&q+aZPUhF6-dB+q%QNXQ_XSDMmyplN_Y;5q}yR-|V~XBWrhISFaFAU8k6$!ku*yc^EJSGK*T z=KmJrv-}|W)j{&|Q29k__J?rgrdiT*(u&d(@*R>&7U2?b7&pUyR-wDvz_&Qyw99Xw zKbNE0@4L&_{_7xztJ>$S{4*m;MhQDpY&H;4L4auz-G8eDr11qq-w*6&e^fA8@^>Br z!b$u0v@3qp9<*DRuxmmcu?6CjG|@3k`KVi=D)YuWFKW~JOaVbnFj(b%KK&4}xuml7 zF64CBx^)%E!*m~Njk3gPT8+5sHpJ|qDdP~aq;(PO9%T5M_-^B_`~<+cm8-v=e?OG8 z*~-cl?h1o^ZZvONyYo0m+b^TgXw@OB-2?`GgGoNA*A^e%{NH5$Z)T`L)kW06IxI=<98b%6lU} zd;iB+CHAF5u!l=cJK>D$!T?2$D0_BP5;hA=VVhZf#%kkFlZ?@=RQAxazhDq`AhEds zgq7{P%O6U_+S`NmGG>G^_TNOB>Eo_1pG_M4=u(X_vqNHs79c<)55!(1c}OC*V*}wO z8{dE%PE)z|3zSu&W$!s?u>Xg-9gr~?|U0uB@mjb^C5Ev3=!e?GFI*zjmb|Q4D zyu~u@3=`&LVB1jIu!OhXiT)16P)2N6vDfmM}z$}e0Zi01L{OR))P zfu4}63BO`^8d`|I>r7G-zM8sey-&v|J?^%A((R=D$5wrax+(Cr*S?+LTU!C?AKFm% zThH_E@opW=^W-w@Hdz;)ORAL#zf~Aa6PkSkl2;ipB!Ak2QaYfg45d#1{WD2wx+u<) zA5zwZN{xUE@R2E}ozxcj?YE|}u?71ENSjIfgV}DJQ@1F~XP8Usa0{iV?=qWQpO2;v zZ%*CsfgO2a=)0Qsufd);lqckn+HkfGu_YUS*8xkbMMbG+PZ-5pIx5W9xDWu(4{*Ae z;MPsxlNSsOfn>me1GePI-i?ZjASVHTm#mzJl7?24ui?0DtQoTo zs!1+h#mj{W!Mq+g-|#}8Zy>e5meHZgrj4= z8?!cubAI>-pzZ=nX>G6<7U{7Tqq%Fdj{ zJ6-jjMV`da96|v>(2xaDnTc#7lvUN*e}?e2EZ#%xDgF@TCuW;Nd)!MzhF#ilBPbjN zUh&S~9u>OfdG`);J-nG1Jyp5fYHt>9{t)nNR%I0Sb;+PHh2|qcnGMo#QJl8w2aXxPeRIhTR9(X3!3R|_iCoR%=rf{e*YNuQ9J2MWPNq6ar z4!pI1Hcme~o3T7?Cn}71MA!X4BthWHg7F$S4~b?XA~449yUJQg`8$lGAYb32RT5)I zYp5d03mRD>Vh_R)3Wq#$U)jJeROYo@y{cnAjje|rbW=m_5v zdRhre4peW9JI6TY%}C1-uZa$T%TOO)MRQaN5+_TXK*8h&?#~4G3<`vF_JKn4B}QuG zWJA+`gV)!p1{Mu(u^pqXhCoacn)1(OF^k+Q143^xvVp zbL#KqOr9Ywh(R))QuiPaAe%G_qZz4~f;t^%wO@@YTXY1Mi1bq`U5>vt73?g58&5gA zGXtii)TcZ5eX>j{;)dPC|}Y;umdv*NnW%@a{bJ%bE9HM1yc^v49`?q&f!})o1m8}dVgcOqEpVx4TXOF@ru2`4y|3%+mhgT=W*RK8 z6(O@ep%JM|2AZRqIayLNy6|@Ka`{9v@5Cqi3d8uB4@&O^R@KgztCSwA@*G zejM6|)v@YSADEAE&J1%pcDX={?om(r#j7lDc9prji1zFK94xnCq5@^uO7aSZC05 zUNoyxd;YU#6dH<5$q{+ee{cxV;hLJs1^_YMsC=+b2Myj7GTY!a-XaVP@^r~n;5w-WnAY*kzmT$khfH&2ouL;on2i6_id@}sdR_6ReKn5@%}+F;L77DhvpWU# zR~PA$Lq(#_o)&Wd<$LE~$tH=!EFUNI+jRfk>=llRTR6cNap8$|?)VBVD91|dUAvex z4XE1lnX>E3xizcj@L_rUw+d)z`dP94nYb?R{>wC-2Wlp;wi=T(-|~XCVfGxN_6vh? z%O@zB3xze{mlYEogz~r)a~g_R!$qCdnJxh~9m-+< zUmHO+y#4ztJ!HJx;|xB;xnC|B?y6|d&&cRFbVA{Cxacs%4@gSJABt?8;h}6>RY)}U zb}k9K%06AjC<<$gIWC|eRg^(GEI}<5tiQ&0=7o96u#nP;%kfs=YF1SYoL;_|fqk%i zcYjn!!PA&59|J*g$S^xB^IAkIuG}MgpS-PX%t$xj)nXn}Snn`HfyZRcbwbgi^)=FD zs6EYAuv}CSJnQ6K_r6wz`$U7Gvh4EHB^h>UCRfN0>oF8QmleUAP=ENiR0;ep?5Ol1bMx<)P ztE$4zlNy*+vINO|PA7Ftq~gOIq0xAyhbD?C3aK`Ca&m7+=AbkI7Y(t#-b~w4x4H>u zZj^{xVV|S9z?36&D-|;2K51ql2!9gKrM(;xDaXF~J}@LE+sg!Tq`(lp4;Ai?l>b_^H}p9?N?P7 zRV(TIQAf_v`BC%S#^2;KEadAi;3bMhZ=9n7j^D%HhYl3gyyy<+^p#}IH+p>p4I>>- zw{&}XL?ScctP8us^h=)3WUiI)AbUe~H~o+&(hV9zDQ<)?dmhg;tZSyNkSKf!btpCc zm31j1>wLBpRv`YAS8^1dobY9?6!C7|e{PfB>sVKWPadRukA#v!b(vRHhXx<1k}NVz zA&n@DOMSSa1CaEZr1Qc9y0`qCHF0z6pl^ZoF$ia4Lg4a`fI&`~0(aoLagn+LQRlq|N5^ zAo?@Ty_40YcT(~JErnoFdR*_*r;T>$0D)ulk34{L2mpz=&?+f^;>O=4ZRfvdPTZ#M zx~)lhvVJ4yn>s?eeeZjjL=Y<9{s&aT4?=5{ZP?qoUOTkK1S_$(jNz z*h0Td6Ql>gJg;ZuO-W6E2>{ur0Ok9R5*P^K&cZ-$X5avZT%h=U!L(!^9B-Jyhlz~s zj9V8rTdqPRthzZZx1Lg6)q<1a1_o5keeHD;K_r_i!DZ5-6g0+b0Q$R*b|>%Z>HMFT zUP}nh?9$2{7&Z-IJ2+%5cq_Hl;YtTzhIJKRG7Qe5N3Q_~%5no`Jsq7tz})-WD7O9m z1A&SYcZZZ4FE5lR#{yqqy*2uG&M%%XD>_(xw_5yI*1|4wb;yuWmVlRmS0?QP++|gB zKYxLG@PAH&(tK)a1R7t+O?NXfhvdf*9}gpO7D`)n|5rxvc=^t{UL!E`&pX(Tml8^17>keUn3>qx z_9L=9pXlpN>w0}2baie1xNG~4aEF#*Qx>e4uAb8tATslC7%o9xQ!$=jE_X*CVQ(cj zt}IhkSE-cMl?pfKZDh11MfN=`+faqx>Zx1Ou+!y=nyU5fY>MsY@k@|BGrB%#I&fMy zf7hQMyJvp?-Xrgd)H@t_M6Yz)-%q=y{(RZqbke$g)YT?gIsND76uQQ)aAI{;TV0Te z@t9P)qS(&4Bf{aTRn|ste}4HEdCt|Ps-evg+l9%YLdZI~68eRYJi;uE+=( zy^}oQq7v`}YQUPoHF>1bgKy<2UAm3$u`IoWwkzme$12f8jI200yT!cXn)Vf@plwr% z-BhJX%=S6ry14`6?As!${;kAcOG{^H#qcJ>TwY;4qze*QhNm77#{DRX9CcvsvmK>v zXHOd}i_?jQ0%(1K`;y*ys0JjN1KW}kq$CXAMaKJE)9GT8$L0*PTpikq$arjiTgC9c z0MXNIIk91iyVMQ8uU zLx2A$raTpYXSZbU+t<*ba!q?oSJJLW2WS#E{5i8%_eRN_EOSx@h0EWSdPq0Yde526 zMsj0FOZ@-%8sBdjQ?B9TMqw}+!xpW2vVoOo$3vn|?*Dyxxe6SAQ39 zr}o=50!rC%N7bOy()6@2%<7C^)zpoujsV|rSO3JAl$Z*CT{W0^43YrJ_Mn~?;Q2Aj zd3Dkz=BEy?I7rBkCljCkJEYP;yF5|ucJ(;9gp94ebyloA9_F{nrbSsP7Au+WbZ)t^ ze9qsp)l0SXl?>D$-RZT}Gb)M87O3hX+x)fy_TH-_BOCf2@VMIzlF*J$*=Zt8L!(BR zTETTx2nyZ7gQhq1?GWmDTs`;EhQ85}V+55CSXm@0=3d%KPU~pyaU2D~hiJ(>hp_C2 zqSERdTekq`t%i}cCBccsRay4VLGDNNIGk-8UXIXnAFZ-=7uLeIlanMi33PpWqwGzZGc^&=nRnea|NaiXT#nC$KguRg@; zFjIWnUqNM&XRbUl%s3GJK&>n3u{D$lGy7*ta5~oM@T^4#>P+7MLU#X4uda)UYWq6k zz3wU|dWDqT;HmmB;tp0I3qB5^%}2CY9sWZ~qv}cWPqOz#awYkt zVfMKTxtqb&36J<(y-k6*{Go|<^2nP?XLx;d4Oo1rBJAW;$YLuQ?P3oWpZMX9ftu~R*EY_5 z>qxKAn}=;AoSJlH)-f#}#G4B4{I$Hh2uEFMx!joWsF~ooB)hs%I&KH;M`>RX{u zppQp9s+yUpG8&cB;`Wa`y;aBL<&N%mu$7#ct}8v{IlaZZ5 z=Zq!ATK!0?TvF(_71yry!WnJoSz3fFUExbel3UtEw-Cd>$K)?;JKtu#>kZqP{YrS_#AOR!cJRfQ$C&JWVVDMyly zLYXAKMK@e#{8`quROGJhxW@|h21{q&-^sT-qBk4wAa}2+LTLUe`D=yE%`~!&m;dQp z^Rse1!g_VVt8}YVd}~=Kb&KS0C0xZ>O05*hZ^(wj(LXfpj?Ltv2gj zo8?Ha&UZ5`5o>v?l+mGht-Qj4$}B;K*S85};;G9chJ`QG=>2rtb9JnpBl?`eIEl08 z=F8#vJ7>(744v9t$Nn5!hks;X6vl6}u0eqaY>4|9XCt>DZ~Z{tULNz&c1aGSL$$ev z65-Dm;A_w05pn{E{A-9!a0?dI)PUjhOP!6*ZEg-q_%@``%^}1Idxd&YNmfpta)EM1 z&RUkbaOAbpSEY9-TX`D!9r>%W4Jryw`9t|r#SViZe<6Rv*rQ|A?vR9|{=&j7ajm`3 z9#wZr`#owb!W-}fozU3pz0hm`9__JPUUN*ob?Iu32|rp z;kgF3`_32QV@_zB`;`4u!hd$xDOa20WWvcA?On%R#~mt3*&W9n#uA)vzN8Pqkp@@8H+}ttZw5(A?hRnQ>%D5kf1xQip0-5#VERy0HuB#4XRgf zb-G*_%N++ublNIM#GVdz$~vmkTjRb=*K(NNEugEZdHhGvZ3=6HEjCLRzdeFE0oX)7 zxkqdEzTys>VMG}2Y&qaOYTX-Em=toaod7orjI7}FYP7j3?FLS4rMtiskCPWEIKdHW zkTR6eV&dsj%fKEjVTzk`^Y7?1WFRaVrU76Cf;a{N8y;#fUq(YJxDqy{6sL(Qzgr|< zTp)2LI~YSUY(&;c()klTBjOkFI^I@rEht}`=}2MBxg?|{J$Jt&7HtMYDna2fN{boQ zP`M?VbKqnur#jT(B?*1#y6e$2szFjX?!3eW28EfE_{ z5Z5feEJ4dm=;L*?TbY`i`5n))QA#!1CwiHc51K$u)Sb^-%!#K(M9x5?C{R{pY?G{9 zI8Ny%ES#_@NnN&NtLCIm^Zw7?Sr#}eyUL#GU%Li(pajnQ?EiJ*rHbr0*CYGnEAue| zWbHU}Hi41@^`6J98-3-YuMD5!(ezb$i}Ge;kinU_E6UXSAt{Z>rnBBLo3|CdTj#P) z>#+3d*L^d`u1QC%+jU)z+jxH7UWLk(m^2EVnVWHB>E@UNxLY1Rlq`Gft}!F=UNfri zNks3P>pkmn2PCm2@}SA3!t**oDuLcZX9^2a$-%@x43$EZhDiO6m_Xzq9#n4qn-$u3 zwrt|f%dPMg*kK41v0d)X^U18T!x8iYdNmW93$@Z1@d$f*-xkI3G13H5CV-D@o?KVa zpOpJ&g7BCCl0`|`k#s4C9-;_@IFM4PRB$Q-SxuYTi}&+2B-&RZr>_BEkOW6iu0HSQT6zh@E+HVE_|mVKdIxxk8`>1o!DGj-sSrnCDQ&I zXOi=DGG0uOBRfl;Fg`o7AH&WekdqSmQ&UOR$NU5#A+Oa3NQXY4Q`HpCe7r)w&$Y$1 z9#KxO2rMM47A#8d%Paw{pLz3Pjy^%6@B;TDR0rTw=z~q2&(;o0mcIVc?FS;mN$jhL zoGYn2JEhaS=%ril>EShyttwvSo-rYb-8%qn$t^8EcVb>;nW95!=uZ`UuXQ+NQ_LD#8ldFQlyV_ z8HXb>1RRuE-_{gBurj>nfll`}UR0XDDRo=S6+Sd5ZX@FnDtDj4vPxo}(%t{AB*>(d z)E=s3(*NbiN^unI%{*&L$8QE%m_qn0VNpTH{VTY6%{GUaZg zuKcylw5TpaOh234XZoLP(=yv!^^_y0E?1bU@>yW%9UfOlfx$jY+qzNL&<0zYOH9myL{1h`)?iN&`dd|p}^n! z7iWqFt?}fCgs5W3CA=oLvS`R4-gv;)OrWhPdkYsRW^eYJf9z13NEw#vp2vP{7nYM9 z@z^+`AT4w1v@^RXAqyE^1G zVw`VIzDvSXlD}vkciQLJQ687Z7k>%5uqox8f!!zyy=j=owihOFIgy-@n4H}nMx$i+ zNr1riQ}Ca9vDMU~rRM_Hb#a>)6=&YvwCPqv(OUE-VECHS0RM1( zorRg7`C$_of#;R$EI$ml@aH&?&=3{}=9!!PONO3bm9Moo%xB_11kiGu5mzo%(E(|W*UN~m%89UW)1r-Q6OpSdONsqpjp2Ot(n^TqzQUf6`KywCiL*z>t6&C{%i zl^o^l9z^GW2ADjOt;6+-B{T(sGCl4f9rw~S+mk;$^ z{DUY6{rJd1(1Yq-c<;e!@mgz;u;U~(pzH-z+=z%j16r!JPW}TrHQZXizX1Y6<^?BO z>fEHteIFEep{Lq@NJZn`0j*X}C-YA_sZz!L7^r+oC9Dz@*r6B#%+y0JUf{XM+K%O5 z%i3qnkSH@DwvS;Aj9W0tm<|xay8t7gsAFAfq1ziNn1Nst8}HI`b4nqlDr&X`5))(f z2xedul)Z1uE9MQZ@9iBK85=uoc&NO%c>jSQwHz`$bH)`l)%uP=gGf}ueTlDLjo?s$ z$T}5ud;K1)P$#w5?b-M*wYsf7Jq>*bN=t96o0S<2VG8A`>R3+Zx-H=ZzDv3TI}~_K zKtLVAwuzKs9gFZR1mcOv5vZ!nbzL3Lx~ZL2ELrwDN$p|S%de~@7J19UTnUIAz$3Xb zBA{fs!4ZjJMc%bOP?dhKKW@dKc3pQ`#P7^m*Q^50?~bvs@PM~rDTwCYGo3SZGSKnk z?+^E_RQ~`_rlfhpY%0L9PhA9Y0^}0ZSl-pTiU5kN?3J{ed?992iu_-l6d{b!&^W!t97dh zt7nGy_wxIp0OCNv9gF-c`XYb@lTt1dK~s=an=7sdI8z6JnXxl+3Q#O@-IZ2egk}Z0 z0NvAKnfBV9U1WS~unHP@bWsc3!=yc;6FTAu1aU(z(Z1hH`ZnY_K+X}&rnLV!+k=fM zuj4ibZPja!&x;?05_)@ycKx-r#X}Mc>+MGqt@D(qX?TwE6ZjpAfQr9ybd8y6PZFl%4DfeL*&Dg(7b!f@w@i zj2)gy4>kF`dEl4hKLCM*hk<;r)>UOKhti_VXkzQIEM2{_TZJ zSRGrEJGS)UgfvCVXd%c#L9NT*Y8S5)TFE?oI%csOp`rtcAC`KWJiqwjRGUIa5yKXTRWOv{SP zW~}#b%gqQ$4{p!(NZ1vb%^hjkaaCt$>W$?o(}$)MX&&`08eyybb!p7YG%R6zo*-_% zStPKyoB2rXYf2eo)Xqu>0XRU3bTL7ad5`M*r8uKfQO+qS=MBMea{fHE!s)9gRK)+3 zGEr4UzVlRwsD~847orT*s|ud!(keteAq12X;-#2i@|3Fuxm}VlUf-fCJ;$r{s!4na zUcM4f{b6{cyC;|9iA2y;QxZ}&f_wc(a05#XI2<80k7E^_AxkZi3@j^aVRxL^>^7Ob_S6Y5u&tBC9%x@o1b>UV_z88v6zBou;Epp^(tqoxe1)JWq zLX6^&05_3NIkO?P_-9EVGV6l`X-`5QxvUGiDtpMPA-yKLM%)l{sKHaApYP%5ZFJKr zR>ta)V`zM}lFFitCJ;qEqpd{*mMenOLQ0?}Q6evK!eo)(=gmy#4Aj$-=1%U@W5BBMycfgJo z<+z#TBC6zRsx;upeL|I~S2LO4tnTCPTW>U3X1UBFiyi*b(lapwM1ODEl)b=m!Cgax zs)TUQyg_+vu%c_pH&Y-?uFYz}stxr(**^XGbNVI!@#-+!DRmLGLAoH_IsJ$&UV9oN zc=#`&-lj}j7GUBqFRhj+iQGTJs9DV^hS-~73XFG2d*ZER&16FeF|U=j+1>c<+K}2u z@Qh@I5^9OOJeK2t@fz}^Qm^YU@G50lL$OYCNhp3UmL))Y2Dz9MFs%#?Dv?0Jg6 zV$n;z&Aa&yk);Mi$il9-nupzPd` zE|_1o6$aDR|F39^B74{v`DgM++YxH6-RBhHc@PHS!WFHDJ0Vz%JBr2|gZvgl3P`Au zDrfd`Es*{@GD$nKf$(JG`c#tFSn9+j5?tM87gVhG2bG)0no@J1-);F2$1UzJERG$^ z!aG&4y;ZW?-}$i+#C9!vg{PA}m2OW7If4M4@@s$}5mm11m5`mP?&6aY9t7@-65;LE02$&Il8gBz;kB!3emQ*ocX3=7?L3q^K^<&Wvva# zUN?1o&rq%0|9-~Q#t=VNTzFlgZ$^f1XC|I^HBYD3 zZ|f{GmD{RpOjP}!*2A^j8HP@71^HEAdZ%1e7tT#@_oYT_{jk zoYC=^^mrvQin?FQ<(`=5GG{>kMZlkz$!CV7NNT&wbm>j)`wods5$ZPfMozvB+hbn3 z$_4P*vb^oB@?(+J>#Tn*O5jA)U&jS5EAgRBQEY)vkpl?AWaR*0b(6cNAG|xM;nt>A z{bKECm@DWJeNT{G=H|2U?!oXA4%&&swIR$Ie`08u3B~;4AJYaBj>ma2FZLvTEi?nZ zt&lAOf%g)qqT3vOmf#tDkbYdp&o6E1+KA7wzyu&(gd{Qpp3RivH6z^TzQ9}$flyq6 zYgn_i4vfEaculM+#+4LLYzDw7UielyW-I#?baRbryb;>S%auyJsS~XD3||t4~R3@K@<}WEJcd zjW53+n)c0Z-w?3!@hQ;xFr@qIP$O6}Klwt(hO-f=DT_4=G?taDB ziL0FtwWGmVSeAtY#6csIUoe6elBkN7YK0{o7b8l^^Eh9nyqRV$=kLVG;VsUJUdArq z)+Y*#WOc#*?BavacnB;#a{um}vLlgYv6Hr?f$}OrTFuJcg~bzFQz~l=q4l-I?6iRN z=txez1Q%4YvL*RNorE2g7WsCJL4xMUV~SGWS(G+_;s9jp%)6^u+_C|s02>sC4g&o2 z%I|?6ij7Am2mcvk1Bg81^lzS*kS5}6^LKTOy+2GyT9mVtZk&y)O({e#^HrR2*0MXl z8}__A>JJ4CkL-_(?hL%f_GccAx3dwOxZNoM%F*4Ts-LBd|GBq$4tIQBeq`Tl1Fse) z$-Y42ook7pXevXu7dHH!|z2d*cX8Ip# z{kDk+QwQJGz|@gMRJxTHo|TnN72+7l0D(^>NgMu;YJ1l~a zd+L1`ge=mW+&!(obC2F`jEOzRx=%?v_9TC*?$U7b?ZPK%CTolz+&8Y-`n^Xk?)I?~ z=KYPj58d|7bo2leFzOp}1-0l6CmpT)Vq7_cs&apk+wKi)XKGK}+AVSn-2Rem@dINL z#q5j2H)&&SE7Ktrt3;Pw)%1zZVKF_?q&0DYi);pejt{L4Z139!)uW>&5tWg&8q$&d zYQzag_heKG!Vh)=FQfGN3H690_Uw-zsl86#zSUmA40w~A>_VB_ic2YEP&jVFGdTLc!J;94=7^~+UF+< zNCIV!sC4bz6>ob|mVG2|MHFKDu|Ju^*%g7ytnQ;hp$~Z#vu4}=nz2JK&Yzrn-PW^p zH+tlfj~$O1lh9a4wsxVi)&APsEmuCjxvgJ*nQPCZl*sXqh?JD>zp8fba>$!$f+iua zDk*`p2pw`s_3YAOK;`VJmL*L!(4BLWAx@jU>pj&oXv8I8fgM#d2C|Ni^?6o&433TD zaEK2G(`zg?uGZD9id`#v6ZZ7RMb4L8z!TJ7+0z8d)&qHN+mtRU9Z`CfO;5A))xZDg z5Jc}0?%gNsRF(fzT%s_TS5+r9`;@*qnIqw7&V@l0CCWuwx5}I~Vzttos}wd(F8f|_ z=hf}gw%S2n@nfyOw5crG$6I zp%;9$_}WhPcK~EzdnHly31gpm*wJT^{Zg}@pq#})IePD)ShWX2PM&-<`Pq@P5rmcNLB753es^X2f~1W|_^o1I&Auz<&NSHfmi1H{v*L*{8t1yQ(X;9&T25C| zsAdqu9a^S%sgey+x6K}}eIAnt%=gsI9;-#y+M;z{!1t|v+YOnluowS5*1R+1u|q-Z zY(re*qbEfU&Z#NaE{kF=E&9jzM?(Cx?wr_!^6p4Md|E|^d5p`g(|Peo=iEB~4ErRF zh7%`>ScUd>AIUQ&yLs~hR#8eXxw-$ENnYvG#oGz$Cp22`|5;lZeLnoelWrEDoY?Ec z(XHkg#iMrUtNv7PXIFaLyts14F>4KdP-E~eX8OgQ>Gl%) zOhDwfUV|;&&^PdKYJ_j8vAdjd&7|=9MB=uz3vh5tbn=1119BAlk5zrjBxh|(bdW(% zgS5kTt=-EE9B30N*|O!$n=SXX{aVm=CdFh(t7?2Sw@}6oIiU0VvEDyjU4ME7cN-Yn z?gAhY0DuS@cliIKOq<~k2bjRxdd(nuz=i1^xS-IfA=UUU1uG{kdYoc7`|b#Xrw=OM zt|W`z>W0p0&W0?4wKwWwL*|76731rYZ=NsO_g%q7tY|A9x)Qe|P)@2D$T|%l(#JfX zMB-BrUsE&?I}Xm)Oh+HAu9@BMv+P!1{UJxQsW_L2%A6&z_W~WQXK`JycUZaH!W$S8 zTzU&#h(ecFu=@;$&b!xo{p?gz`F5c6Y}3l{@X8Q{hE}*MBl?Qrp`5C-G8-wq!WLcaLM{2QQ?{dvP@$dI>&A3HC%GgKa ztTc_@6Pv%q*5q>Gt1sfz4Kot5m6GO^s4?rjQ(CK~6i zdwsMs1Mz*Gz4wgQ^`ae?U{VKF1Lt|CtO#jtqE;LlZe@7ico^8PsAKnrVR7J4wd7P6D5A~O2YX{c0+BVIFD-`b~(KTMT)m)-DY;4N7F!3bYEvH=O zw8lx8O++`GPZry{(&MdiRr(Cd6gpAbgPSotJJJa)tC;IL7~y*Bulimk@o|v6LcUr{ zicv)C=*D{m(wCNa$8TjNv?_26*A5mpe6=lfJYL;+*rU*5RQ~NMZVZ*>ea_pNZ_vui zp4TYz-2v~kvV*4t*Vd0agHj&rli=;pMSiD$>gx*yz$ZS@6+m89wm$!o-B&dWfWRd) zBUp(w^adi|w&%FD=xuj@46e86BP{5DEU`oNIO&#!omY;}Pd&uD;)WR9NcS5z>*GDn zw#CdEIxEo);gg;yPUWmT&BAUXT|3#V;Y11w3M+?AeFU{xVAkgs2kg)2)5z)!Pu0FclNz#B-?$EVx zRIcV37GXCe?rjqKeH@89VZ*=wZEG&XG}9j3=QpbHwgb3Jblr=TLi>CC5Z=!p^Pag{ zJ)@C-`z!cKp%?n5;pCV1cl7<~lW$I`F0YVM@gi%kPc>+=ycJ=&y+f5tkT4rhuZsO2 zP^%<_FS~nj%XM4964t<9X6s)fE|7QRc_i#ODI#xJh&waDG+HO*@{^)RCZ4SHZ`tfM z8=&%M$gBxl3p|iOUUic2NB0~0l+0H!Ij%(Fu`Z}fizb5rLM1#qf zAN<)s3GuptNw~=3G(7BVoI@h*V86&V=lrF?-ZvJ|iz@iPDW%5_Z0mX&NDg0$dQFsz0rFIT#po}Z_E^|Zy){2{g*c?4<954(@xJKZV&hT28|^%(^pbnZIM$^O~b&S73B9a06;F7-`6OMF4A)GeU>Yu5D5g*Vf-5?5YJ1dp zePd7h?(6*{Rv@AV`yI@sDV;hD&+cZRo~S6pz4B2W>hK^O^v8hSDyhm_!_~E)lC0r= z#4TWG_`oqKI=_g+1%}d@oEW#lZVx~$$j;q?+9y6^6DYEu@$b(*ET*ZkkyS8`E>WNE zuYc~_FN~yfRVub?qTZ2GF(xKEdz?Kyq#g-T0i_nTkYvM!QWY2_q?H||u~M%Iz@)v! z;-^MHA`*$t_7w<*Gp=CAKV9D zzVQDa3?B2({|te`TO+C0$IRgnyjljg?%FTFgb+DcO-7xl+lPA+;KAHC^8OwI$eEC_ zoZ6}6^v~iOw=0STXoj=H!~b(cW+5Rj*Tvd-#@P#d+_?16J@xKqFg%GB%&8}^@X zR`WtFMQJ$6w>hlP$ud00$Wwk!2}|3l#BkFmhr@!PhX;TvkrmdQ)^}r9M&I^hryi)D zOFzO|K}rzW#=50&H`KSh^I{;;X@~gs%S%ksU|q-SXUUFmBy1^%ar_IpqQSA!jaIQj zAErZ(Dr4_}{7bKCa(aIuku&JphqfHHvwSe)-$t{F4Pf*KTAM-ynNePz_IiCHA=Rl( zkFNM~A`8D;-WgJ|j2iEez)e5x$M6q^xF8d~A2*il3*iZeWK3inNGn*=>GxD{ox8U6 zmmfQwjNiLgwa?GnGmnOAK5F`>S6!f6_XPp^(SnyzRDSpeH#xOMojjXz1(lI$@uwi6p;$ww{h(GIasiWY zPNqh$6O~Kvd^tH$Q0JKT8e(BB{eB806#|h*7H(LOfIm86E^q;6E*~BO3n9X;L*ZtK z0EFL!S`Q@o-0y(;z84DW;nv-rT-b?fwzR8_a(2>Un=$(2z(zC+3ME1y5C|W+LJeyo zy>hZF9VDmpB<#ukT!}YJm8~`2bNBOZU&IW)(JS@!v7;4swY{exitI@gyIAUmMv+dfhbcfG*UTOs)P+I(p#t@!OC)kW`bXDpV+m32 zQe6$9zg=Zq6+<8pcMx9c%DT+}@R6RcS2o_NeM~}p`RLNInW(ciG4q{L3=Oo=aBe-4 zhYTGIVi1%aK0s>*v;G!Dwo=#E#*9J?z&vE@7DUWXOP%N5XL?HOGKFn#1;5>TO>PB6 z=Y2&>N5EH<oBbrabh`Y z3qxPPeo*Rf*7fjVt(nSzz%lTYK4RCYijmXYY1Vdz|C=^58FgO>oXI<8Y90f)FEJ;1 zuo*eGL^zva(I5q_x^62LE?U6y7-n(*xjw;K4$Q;zRFIk$&Y#Y#1od+^r|Rj;8V%R( zAMK!bqgD(btUxLF!RiQs_TYCHF{ly#yR%@@XzvLFrhHm=vXG0ahWAyo|7r8L4<2Ez ze|z{{=d%7Hs+SNo3y4_vAg@jLp+s0_Y{_c^VWW_Ex60Z2C$Kp-5+SFwF}5mTn4YdOpVi8d2WxACwK?(wTJ7cuFiuCig@(&A zgEey5VNpsJ3l760&i#KYjuu+MEUHha>Cb5GPYvig`Wn_)6$d?Fr%%7;Fo?knjuhXE z92|_iS3L4g9n3qx%6nV0z8;+X9Mfem#a_2Z=g7|8tiUaM3_89h9Nd=mR-qOdPaZvV zU54|#wa3x+G{%ohMtw0+tXBb0%6Z}wKu@K9YxnV{Tkk7@xnrLZ3`btN%croh%9}h$fRAg3r~5fEUv2F?ew`DbVpE%N4HtN`|X z@7sX+?i$ArIa94w60cVPfgw-I8luvbr0HO2z`8%1FPJ@_r1J_O@NdWYBKMgZ29G*8 zg7`r;0#-}LBc_p9t{=9DpovLw^l^_%g^umqc`VVmgF0SNL3I#*-`(pn%^z zi(q7tnQSt3*xDWcb`3V2HDc2J3z^5Qt+0Vh)Ax4k{O!>ek8cZzfQqim4V`ZjqnQdx z(U7G$5Q^v!FpB8NO^p2c?FoNVf63Sv5>6lX`~{ZOCQI)--3 zMF?UJO4^h4Fp!i>B9LI@M}JzM(bsOF*+^DaN~^NI7L!8ku06qi~X2%kd{V?eTHWTz%dFj>j}T?yx{aH-F$- z!1EKCceWN;HRa}>-su}K6gHFpzSEe^>d=ybAhaqe1GDJtfb)8{M;7W+JOM67IU?ua zLt)M#dW5c{id(*Z#ZW$)lHIgp1CiKTLjR9q%rtBs5W zfodp9m9*8I8?rixaawOBIU*p86`#rCgU{hKX~5E zfLHS{O)aaXH_{p(*qNT9?nrW0s4@z-krW+C>a^}W```%c;^ru~+~&Cz2JH`=4K;On zcWOd(h0Fit9Et`(k+84Uk8c+bhV@)!8#7tqj{3DsT<*%cYiuKP|8vmGf0Pc(ugn`1 zM-vX{V*f8|=Fr4KS}>OKauv=*xoCw%*cx#;;r>_a^PkdsvqK$>9XKFBtjQAq(?b{P z1vHU_w&I-e6^br5qrz32dtawq(GY--UwtDXe0r29F*3MMhmW1F1iG{Q~9EjEcD;1^ddH6j{7%L#klChR8DOCnXZb_w0aTTWQ>@HiwDn zXiP?u3auGPPhGwKgofVdqYaHs6`kSkBHP?m?b0!yP~g=H4_grO9=VMrfBomA;m43jr2Z+86zdY~WEfX1T?JdSS5b7@3(9@(KUv&Ewa!}^=C z@YNGDZC5VIdon8r*r%-S%XE?#V(@^K#Y&xm1eRmh3j`wSy~_nT3&qaEkycKV6N+Hs-MIds`6X-C(Is)myLbJty^QX0>P7dsg$8M5?956AuVueKNd@&q@_h!q62|?-?G{EKJ8TgR<=lmw&r=_zjry990o;ft^oeJW!XNQp~8D2yN6oL*2$1klFP$Ib8h(%=6y$c^E z9SBn+mem4qOQ6W_fJ7dc+W|!Uqze1UnhX5!>KaXmIYQROG)Lhc^JPHsW{!T|yE_A6 zez#XoYYNvxOabWejv!Qq=aqb*JC@yc=qcimvtdXUlD7<&z`5{xu03pdPWlw0Q(pS( z2H$u`hv}~{7^($k-^O?$Ww-;zxGtJGm8QVrTqp_$|0r&6L1|CjK($AN!?Ap4JMQH@8Aa9@G|DGS zJp4edx_k(Wm^5C1aS43oT;+fJhE^3H;_VxsF>s&{C0oWLQ`GO^BkV@$i~8dC&)6ff zs4b>Lq)GAG% zCM>7Si{DTetjkQUS>fL#IPk!rKK9ZN(LMOWTgTRS+&l&<2}2lu&Ljd{n5CXs$yqo5 zn^z=R;gf%{tX`0uapFcLMTOSc*Fn=1R}->PsT4QLd)4sht&fTkWD3zq%%hh)4} zR8UUkko^dEVzQ6B)SQD|9+UZIf7 zZ%2H-o#7)_Duaqe{pm=d2+@aDcwKEI@7mRmkxNQV&kr<4EvuIpZ&B+*8=b1Q+A`6{ z?Xw2DGjT72RG(eFDe)Z^JT@+BcyGTid_zHArdwk|>N2V0d_f7hdvAZxF|CzLd+`P` zK^0(6t?>*SMmW2|JEzqrAij$^5(E;)fIwnW!(Hx_qsq6@aV%EaZx^3DD)5r}_-wrq zUXg+bjRt zs}9U9vKC{UYi=(3%kOp>mLxwqi|>i1f$!Xx-^IZGV#j;m6U||I1Henb!|L9nWSK{6 zc~;i8yupR1TKTWdr8>9FCt8jbb7z|_0=ofETo*4Z-)Z|UgrzlV%04Kejtf14|32~v z%XS_L+w^xmH(Y}>z8~4(--vnf`hF?c$#EG@O928G0&}Tze)2hgJfheOYYm*>w|is( zhNj=vZ~4QXJD;`3TIh|0umt8o#8Qbgr*?9~txe5=meI2L63T#{my0IyUp}>PJYifW z5ZzK1^IvhFzs+wAKv*JBT~t-xFnPb|zIGYlcC-t3*6RJGbjn@jRn?ak?P=c&hddQS z)8g@Iu6R9TF?KgOiYR9J3hYhlYxCNKI+G{bstUVF>WU1N2KQimdCmwqMD4t$@imfe zj__3uI=VwEFFrX{$3`e4Wl5BLl}jPI+TqZWlWZ`kq%$_L*>1;7N0((PHcn*?FUyP? z?bMFf#j0v*)tcjX`n0X{W%b23a(vN(kl=)r_nW*Tlp6uNXgF)(=TFq0c zLvjk%ltSZ4o3d_nhuYSDwJpsfTH{u`f4kbqcKX&G8%(mSLIE3c`KKZ|#g{dn*uy#C z9)LJj2EOXJc&rC#>R)7D%Q};Mcx_h!D4(}}tKSX!P3n1pE2SwT5+%xlwV5Av{i=nX zf_~nwz83q3(TR&HxAdg9#Y+>Tlvs{~ukSqg&(UYA`!@i5U=V=K+SYm!u*OI*l^nFs zX=_=SJu=4@7UbdY`{iy8U;Ec}|5(5NM^{$TxsHyrfmvNIOFT;MRAg=zow&GJv+d^f zN=-IE;OBDPjhq|vPWxhNzVFjS9XPdoAkD%jgERm(*b+=Y{vkc#Nu?AQb$@#5Z4R2s zkY2spNmV+O5P<2JWdDuB-HZ}p4nJWsXaX;gu*7NZdBr=}*KP(;x{3JbZy?z3kdr8j z{(-f3BUf<-_~!{pVJD6ygusKR@**+z#_9 zUupR8uaaG&#iBsBkip|rei7U`8GFp^9aXe&t^7^>*;pOdkf8-?`ozgo>6@unIy&#s zKvoo!R@uIQMiy^b`(7xJK9Pg5Ifgw}#EUkT$JQsde_T;h7pswSZdX`o zBSt(hd087`3w@5%ml>7RcLn^BBO^zV(9mOrW?HmyHMOy3adL2Lc{&>mzfYG}-gIUR zvQ(uPmV|mCv`7+D_a;#4$`4*Z79Nbok%`0Y9Sy^dOFK>k@$5R(jS-`_ET71?$G^1j z#hG8oLeZ3y!I zIr!2KKxMG`e%y50jm)j5zrxdGk|6RbETSD?hO(x>^k(_Cb8uRYT*DnIqva{A%}LW! z%?zE2exenF<@3*R@AmFSnk+t(IaEI3HZ91nt3`wm?IQ@KIu4F2GPNIFgW1w-^5Tjr zzliSakOP*e2+4~lXJqpP?xT`+QJ^t(OKNuLq7nQ`U_{~f^uX0Vf+JtzdIy!v3*TE2yxCq+3 zmx2?LZ@vO7E!oLXgADFuhj0Py?`ao@9K$>RJRZX#?8>k$SNF?|r3xP5aU*ScE6enB zWo2B_tEVq_xcR+Q;G}N9c<1B3U&`F5BT65Q(LlpRp!gFOz}T3DZOMUSZxE8V`)k*N z1pVct^9@hQl-|Lh@LZ@r5e~>B@eQk=Zv)hL&FJlozmJ^-vaz?bkE?{3W4|B?9Wl#rhXOZA@F^c##c(~_f3A^44sA8$3F=Yvq)2`RJ&I76~~@H!P<-0mJstYKMk^W z-sKgB0TZBoVR*UQdEOeOoXp@X?j7Q1#^VJ=N6~R*JeikR;1#*8w0Kj3_tfuvYGkcg zlALYL&ie#>9tu!z{eYXNOosb&YI;j2*As}Sbr*4<{#7@5yMvCd+RmfXXPZ>?LQ~cW z43IOF(h6MlNq0h_;<>zwepxd2Xo4-M9|&lgk_ExSSZyl2d&6@uXGa3mru04xOC7_2 zeTxNLP5zdtLmE+qnSt>7%*McATI{_ggapmw$ba4 z)47KnvtHpDgRN8Gd6DmD&VU@!V-#;qkolx`T~Nfvh6ST*^iw;4i!0=K2GrR(yB425 zx1z7lCDO16g5L&2!UyWzO^JT`w>I_7nVv$&xDn16db~&w(;2%dxz5GWS!@?W+l%RL z3d>o2*5&Tx_q9OdM5w!~h?hpmOUgYmi z>Vw5{pBc#t(lo#3iIUn=PL(2~eA%106>GSzBJ4=nWSQ33(9U#p+#cGAG;K6Cc${!w zp!zL!oX6YK? zPhI&O*L7gLVKK|yzjQ0m;&LnK;Ar(MF>(?R5;318I+O4Ld6FyC$%e^z+pvXz{l~9jfQxHf$)q$Ogb2+$5*WC2&13Btc zb|lHGdOF1yW+UPX`?*(dB8OU(XM|dJ_Tb4nu{2yl-EaSin=LoZjtvhQzi(aj{?xA2 z*VWyZZK&l1(=@1>ty>FcK=r+|ygG0RWE?!6kGnY(sWxIc3{F3!r2vugB~K?sq}csb z*>s$l@E7}ykdc*@i7ikw)1dHV851~GR7?paz>g7f2uen=i2HLeyl+Me;22Ebi^j89XnvHWgModvFZwFxteCyK_{Pfc`AnRn$l{Z&4W~^yrjq~P04i4Zpid?a^vu2|4`97BKQtU=SAMAT@hYg!+U8x>1a5l(k z(q}(LUBdg{{}lW_cLmPA9Z(({PJO5ffHP+-XyQbV#q3g zT;LT1k;*N|TQC}{og&qHOz}EtP5mBAdbb~5M<8m&Gg_RNN?QpvQB7oRPq!G@8=J>B z8VMwEe~f5`3lqY{!Q7CL**EZwt*40;t%UYAGeSk~8_lQ|*+?I{(Im zM6Iwe%GQCFR)G>y@jLRz)B3 zs#dSsj8h|R7nSjZdgw`zOOz|qmmt4pks!F_i1;7XUbJ0Cz(oD zbOuVKkK|Bnk6Kha)c7r81k~>!B zER=eoTxlpY+10w!Bfp91QnDKHMfQA@lk!iHeX7{aKbI{xi%wg_XiI~7R5UWI*rr`y z^!fLsU!velyQi>BR}f)mg6~7VNUHx5Cl^>S*vrI`Z<0SPWEZ9&R|YV50^yR%glz0C zj^_?F*>#p(F`47~xliY!W(4pzl_dS-b`I^$h8ZYJC?-nae8$odxYcTT=i}WQ7mjw# zgHPv--!4z-8`0NNptNVs+m^UC1z+DSj!*7;(4E`?{$HGn|LQS+j9Ru$Q0Mt>bebJj zeHFCu_jeXCcIaMY8*LR0P}}X-l=Xj{ULfjIKh&6cNM6Gwm|=tRs{v=kVXMiX@6%dx zLr+l#>wYSMIwgGbo6<<=B7&|ga_(B{^Vooo`bkYEnk}vvDj;g377=`jAcR>i8tPZAUT~)gNk>lRbaFvK3 zWD?)4LaDVe;q?lv3x8skl7JoX=$CQQ5$dnY{d+OuLt=6)#YesFT(Z!;@3W#F*j9AdR6S@TTvC6kCu--xuKO z%(~|<I@d0!?Ze^g<`QT~8HQx3YR;=bu2MQm^$aQ*E}bi|yq7K?87K)e zIOR1`-F(r=sugj$^Ap%yeFiYZEoM{$$&hb1?k`=>>__`<5w)(jrLeMxqql7GaA1fgXZW_ zjvEU2!V#?mf)!f|A`)i0DSej9*3%r)yLVD@COY^44&(BZIhx9)@DVSl!MaX4p8KKq z`fH{%V$bXHe%>x*f>;tBe-NyB%F~m+M<(j^NpfhL1uyMtySiU9cTqyg`L1$AnkFsq z6g_0PLKn?PReWp!6$rgew@b@KNcI;?fa7)yDh+sN-vlFNb@|nwtz2Jv3>5G&e8d+0 zMCAq-v8Y+|q9y(P|LB1B`C^m}GWACf5Ja1!6V(gpsp~!%B}ww!q3$(WywZyIjim!W z92<}wiR&_v5hXwOdws{{;_Mwm=RE(ty!y3{ zO7313dtvL9vSs+|`jZOodR1h8n+I1VWOEFnPHv&PBLo z|3{e!zMSRyk!UU&*;xx-4>t=TA8X}|NUNAA>}1A@a7(gcyTggq!|Xi6)&Ako=o5S2 zUXOQo-+_dk%60*Z#ar~Lti@-T#T;J`U16m?8+_%l+iLiq_V+N3ZgWJrYDjU*$!)(2 z<)_E6eG}h?MP0}LQpqIG<`=jx|K^w2m{etqeH&7+1yp3E+52@f>Ge&c|1`!taDLo< z?Ry`q?!;wX3uJcBLmiO8CU-{@6GP)Jkq67jz-m(rI6PuXlqD)Mo#Yn{ChH^3JoTrG zN{>9^GkZ2n9r(P zVNJskC(vRmgm0vq83Mq~zJPen*TUaG+-9HenJyK%_2mtJdY=h$hfPnamJ?W$iA~csmYBI6DmDi%%vn=XSWpGJ$OI5;gcSJwdPv?1Bd?m)mrlW zJ$qNanNc{sn=d;)ub>`RBE8-p5O^f22~?p-NblrO5jkR>OJA>yzx33)aJQXOhx}y% zAT(BNCoiCnwv#i}>79@jCv4(F$c?~cRDW&gndWeF8Ks&EB9o7GLV`kfQjS*W)b-~v zA{NyEK`xZS&V+yB)1>beuI_yWiYqJKXzKy?}t9UZbjUEgSe|1tF`&$~7NYRvxz?25tbyRbAe27dHI>nK= zhFZv@J7UY@v$A8IIK8!;uFzE#&-hkIK)?Oi_omncEP)ih?^`@WT&zmKMw?T?<#o4U z0E8)}taVbxW+J)BL2Gbl_xbFzAvr)iZ3VB&Fx9X_9~Bil+GY$LJS= zu(5Qq>zQjyj)t^d=5&>>cV)U2e>0aOktkZ67U0 zzaM+qMdXXE-m{SRi^~!+B(O4a@kAOIV1Yw%G8S3NUieQ{ z@`=%UqY^ok@;kyO+gKB^0@B;C*l44)wZBY-*1Qa;46fTrGvSyB$(NFN(RSU!j=aC& zs@kBXkRq>@lPtu5@(S57qR9%?Y;QP_pGFKTOPJJ*b$G#`g0o5Lpng(K7L6wc3jJYE zWA0}1YjK`yIlTiswHaa`F{!pLv7c&OHR$c#KB35I#*r8{HOF<>-pm@HUn(9)gb)Xs z#151Dy*9Tqou2zX*1y)bliHDNv75X?7#8Q}CX<=cF^MlxPJYRL z-p&K{r<)xG@b8_zZd9^98(9sDS-EqmV61Mjgy?!Lw?{N4=>gDN{UaJDAK70tZ2{p5 zlnkJmk6~^j0Q_QM{ws;j60EQ7!~I=!pN;eDmxlL9lSupqM)~O5%<^qqBZ}TU5>iqk z^EYF-dmkjr4syM-(x8IJ>>X(~z%px4wL7VW#aO*`n;mmvcfSd%z?`X+%B-wS231>v z(KrLy%EF1C)|2f*5E z35$#~9)VjnVylbnQv7s3OXUi`B}S%VL!(I9^)G_4>bz0 z;Zt4&XL26;b3-Cs&%rH#+VWH+|IFIZt6OJVs}Xt1WQ|SF3I)v=1O12#J3fXC^gMC0 zmpv6?TBJm5Yhi(*-f+Zo2%wfnq>>3@0h^QXZa=F2ow?#!WWk+S@+?L|NjKAE8<$^| zLkfCH^7vpF7x&a36OtmKKNt5TLcQHU-^bSKx7K|$sy1u`od2T$QkJv0L!HFkrb>?h=_O48fmctYHQl!rtQL>13-$W5(BbyiJ}MoRrs*1IF91XV7YsfBa{aVl2s zx57pJzH2CNk3p4**K0Gw{VaQP^R_d?eA^{SWqYY-VH)tjNX6$lns%fag+BmciwTD; z{eVqUm4Mgr3)34~grHgkOhHM1NIlmK)DJ;NPEBY=^bL5fof%EdN2GAc*tSba|5 zd%Da_mCezJ-OR#}B5eCDOYKr|h*?#syewp!p-?V6K2h15S)NpCOho4^p0%JDK5iEh zx5E`Egfd;y$Z2-YWKQw6dL`Uh+8l`BJ0L5q7U=v+RZic}Zm1hu}UNe`mO z=LptzGSdq5EKUf?`+YG^;{mRZ>MEv&WAW2kl}mE-NCVt17>JK7Wgxm{we_u2<8t}k zhE3`2yO=e>c54;}iy6mEDa~O){1F{NO2EspIQ_)1BZPC>#dQK?im_j?!XC+>TvujUx`O zrP>n6kf(ZfC;SY5DVK1NYw{0LRH(j&?q7GP^!vy~O?pd-yJBaRdj5PM2kMk9%57Lq z8{48QQJxx3-?aAE)fi{#%_G-5f|VtP;dT|evh}ysUl}sn2)6>_4#d`5)A05UZPLX1 z02wc&ab>YE*| z00wzTjq#4xcwee33dNraE!<1rf#}rrLC>Ne*Hz+OPOl;ShcE&{W3yKE(nV^p6KB=` zRMYM@Oo1fB_Fum@?w?s^yJuO8^%W-k>^AFHd7i`>XSn}I49ca z=gHReK08-Pi5@6RFtZAuUM|6SAmr9D@_T~cKyi9ccIdqOV(_+7_q`0!Q~}bIJ)p&& zW{@X%7USX^sK)VIDH$%xZw&JAFK)XGZ*H5^hV7)=SIL`3%j>^td5j9#)xL!K>sfi& z?cYH2ZOjQlvHR&piRSs_6lh@}Fy1D3bWyLXRg>DSOkm@f2&XQ#-T~XVg*Xa+Hzzm> z(gA&X*`GJTi-N~5ukS-Mho#wx7!m1QlKQ3LjFDcuw^Q0VZ0*zsb4BrpU(-i{iRjxZ z4wO`zbg%Kr_q%?k8tX1bhjnJ%E;{f`!2~Od6BuwtlWYrt-E_9gK&;Y|FbP3`P{}?M z?*aFreO^3N5_5SLsoPEJFHiDa>%XbLV$8Z*TJ?HoymC7LVZcg7WTsE-x}QtvjkteE z)emmI$xS`a4?+LBe*!!~@gDlt&DDD1dMDe?TRB)09>_d7wn* z>B%%mKS|5ch9vpQtJwXuLJjOM2Z}vQpox06_V}qN{w1Hf;cu>$RMe=8G?PF*FVnZ< zlGv3(nC%)xH(B;wJMqlj{ebX1v|JYhFlX+7n zbOM7NWBYsG`uS@hqD#v^z^BId-Y#pPr(%W@#^g(|t?qMl-|B&F%?8!`c&j(aaz0d{ zGRmQ$2!<3KgmgVe;%z+tR>_L5{q2jsae_f=KcLhRe{PNxD2qyj1QLQAg#pu3`yOas zD@2DAgAQrzZLUC)(Avl_%KNLYno*aAk#w*|2=AMjyPsokxx--ms^V$9V1_pjI3=1Y z#8SZ|$E_JsT`3M5xPrvD%0an8oi56j=9s90h3n8&sNajoTxSRe2822S-r=;hF%2DM ze8e+Kre}(!T_RZ$(U4rL|I%ZzEV~EFNNeM@N8t6~7*%c>!R!d8lVXBl zVJWn=l4EWf;4AzSakR{LSO?S*SHc4=Xh6ACdK~c8lySDg_f`pkFa*>HU#k^?Mk*9{ za)hMXOej0CYjHfP@rr~g=bzpZWd>K)z(RWS24$;J{WoGXRRr;k!7#8hjdn`O-U8}5 zo6@7Qu$vlPAwxkd&&~X!a5-rWMK9dA?DB9=jmEx5D3{D5oiT{fXLI@`D=Ux#grhuG zD^+!nEA~NcC)v7i@}e#|#_(t9O%4YG-k=tCW>)%JiM~ScnO!i>TNad-?#I#}>v((J!f2=gHwtwVc_EHLQC){JFeq7&ps>W$Ag5{AA z5%-n%)m`Uk9s6B0JIB6kaJrH3z;!O?qLioid$n=1i4lrqDOhOBjy_{)&~}-)5yfq~ zDifYQW_zyMSN{T4L=Pc#ME$CI0va)*OlfjUkgHml<^y$ie%U+w2tv?6msX5G3P$2| z#}ZAU`GSWiS?V@OD{M@e!KF@7;%AG)l_V?oK94RRx+$P-W{4>of3`BKkt$%=Cw)rH zdIYbw;3}9c=gIK<(6$4kYGoOTejN0P^d6Erc!4g3XYGDqwO^ERSQsi+-!=}GN!)X>w*ji{P1H>wZ{UH6 zX{an&UKRFSLBQ>AVwy2F&Q`XK_T!efPgBi&dArxpzkCbg)}*sMQ3d!ynYcWix z_|npYGkjM4H_VCfl1lDfoX0C$VNvA=MKO()qiafz$U5Uzd^r!`sw6gjbZ`=$i^_!5*E*mpvGd zg5%DuZ3wIxm4a&5e0xsqmgD* zYGLt_w3+$h0%!yaVq;0um3t$XEA$yK5Pw|pv!C9zSh@wc?lNT5)5EG6KfIzyluy3k zUv3{ba}*4FG$(pmR^nCj0s#eCNQ4~D zqf!&>E;YJNTW#siz8Z?A8ZLGxgC714l~`@O#>4Wd5=#=oawdMM<77yT(2db7k@4Wp zE%_OM$dm`us47x}?QgqM7)?HZM=$E)8)}u-P|8J5me;Vs-QgJLa01hjt`-GZf4WXYs8)21~d#k7r)eGs%T zoTM@mjdY}?b}Wv#jHbE*Kz`zf{tRkAt>Qc*%XqotdNs+gjp4Eba2n*ly|eRwCt$ys zh~nX>+L&#zD&EyQzPT7a-T4FSO1;b<&IKtjfrbAlppEY|+K)W=f(08x4LSchxPcZ; z&=#FTV)*|ywEy4&Mhf@OGx`^f5+SBVpmLE zI=62U*W>|>NHHU*R5SE{tCw-<<`9FC;fkJ1!6_8;hau))x%lmF$sfp7&pD(kD96H)c$SxIVbZT_~A3 zq=}nfv}2Lwr=d1$v7i?b+##9FLkXQFg^h;+o~eoUixID_yyG_rQYZ@APz*{54#pA0 zKa>pR#RSC`{ME;>CYUt;d;KKSEM)0R4s_P8I^L$4pB(rX9NTKK(#8fN{R*CJBK6fj zg$x42U%7H@19J?CBoA$x)b)Wp621#55p_mM7E4!7(moooafA6ECF-Zt^1qol{;FtA zId&y37DAx8Lw|yrU@Kx3nm!Z4dtT`gHi}vb$}j&kSBP&eGZ2SUb=dNsnEsur&WEKT z)j_QnLZ)5KOXZBcM8xs9Gw{W^CwZ=9$>@IzmDQpcEd(2W&^0pw4EE)QCw7R^@bLL; z`;jKBD-xYQQ2yd6a!O3cQ1R6Y?8$v6opn%hlyAYLdyZByBqP$wt`$?@3G?GqjI-WI zFr(&N%W-LTiVx^1Ho9CEPW9Z5AOL?Gi|-iXg08;`9bHFOX<@)jh53F(ufGo7X8;-H z0l)YvMmC@|H(*Hq)5~Lc+wpVu7B-~+C=Jcxyn+Svys26)m~PyI-+W15v=_={`XO5l zHTRU5<6Q%(;GtU{_)M$_Z@txr^r;MoqLKj!*lxsJ-o*}P>e`FX{w*=TWA)e>mkquq zR>aObeoL>tvlW0b{B)@!*Q#MRNDVE1iwYTY0jEF7nOpwz-CzpVB)}t%DHnxnklM&j z{5nE-m_I0{MuyF@X{w^ZXId;$ZzxX3PofMm&=br2L2ZV2EG&HUL-^jmzMYczD$O`Z z?tN3awcrjqUCwXxK5<+SI?>|?PR!D$t||ghxxLKVr-Z6Dw@24}CgX^Pq}kM_7!5qg z%Z*9SS}A#;Gxrf6Yzc??{fJaAfRlxa)hoqd(HC= z7O1`LmWceuZ0Io0(jzpSr>;rS>W?x`vcp>fVVJl1r4thU;2&FV>(dCwX&XK8S-%w< z9R&H4wYnRLSj%_btvh@R$#$Oo0`rfNf}|CtyFYe$!fDRQ{TCn#B2oP}ys`rt2n8pY zPr*hy=n`c2!FY)-Q6avwsaI|ld#8}B@=2^@?xy>AgA!eO(n7ietiyp6B?7 zzEjdImQZsbH{m6+$_l~!C_p?uVA-?$aetr2!i(>2oJ8*9svS$rL?LjaYe}8@!`*TQ zq#ig1wLj@;6j;-piPNt2DLzE!!*!-C3&;{_h7O&)YC#HO4{G<&N_9zob7B%}yt1NC zn%`Mm`%Yl-g?yhDxiV;rXh^>0f5my?!*A)t)TMO`3`(N+D9}1!YxNnLK)>@{8hpI5 zD`Qq^)g>Q(N6@}yx=%cj9sNvX@vp)=nn6ncK;7JEiZgd^P2j%)6VR%zgBZHuTvAw6 z>wG|E*}P>alWtK8B}_gAdu^xWy(?U(@8_IgZ{Dg_YfH_i| zcEU*ZONGosHYDv&Sy(wA_rub(!|ZW;oHgD9RV~OgubHzEy>?~?K2bePVezxt2%>;P z-?ra7<4n?x&FYaE?cEGI)-)$tD$5+muBu}U?sPHFKe+hV5?aCTUXV`J=9AHC=o-*Q zXUuT@-0>M!)m+!o+T(oHaeB!5lJUF^EcXIqSUNsvI7$4;|X#{w!e5pUJ_ zak1J+C*mxrK*L>l)}}XDmB5!T;U_ev;jCB9B2`6t)Wa`7=7pam>YPepUHy>E1}-i| zx=cTq2|P}#Ey5pcy4D8*2oic4dykynV%zxoUkQ#ZS%}$Wd?mL`_nI;G*TmEF^KJp z_vh{DE5H7`9RZOzAku0+?DJ`Ocwh zS7jB5f%YHF1(sTSKSuTtezZh?ey859@nDV}*wx8We3^(^>c;D^k{15Qf0gLJdBw#% zK4AOfnWngIHTLC=dT)#w{3rZBSpE+*HU0+;Htp>`-fzW8*#W`aU5e&a;9&m+kS-Mo literal 66624 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 Date: Fri, 6 Apr 2018 13:30:47 -0400 Subject: [PATCH 037/342] Improve Markdown extension error messages. Fixes #782. Note that we mock Markdown in the tests to ensure those tests are not using Markdown to validate the extension names. We don't mock Markdown in the tests which we do want Markdown to validate the extension names. Also ensure project-min has all relevant extensions. --- docs/about/release-notes.md | 1 + mkdocs/config/config_options.py | 12 ++++++- mkdocs/tests/config/config_options_tests.py | 38 ++++++++++++++++----- requirements/project-min.txt | 1 + 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md index 9ae74fb152..6f0f35185f 100644 --- a/docs/about/release-notes.md +++ b/docs/about/release-notes.md @@ -66,6 +66,7 @@ authors should review how [search and themes] interact. ### Other Changes and Additions to Development Version +* Improve Markdown extension error messages. (#782). * Drop official support for Python 3.3 and set `tornado>=5.0` (#1427). * Add support for GitLab edit links (#1435). * Link to GitHub issues from release notes (#644). diff --git a/mkdocs/config/config_options.py b/mkdocs/config/config_options.py index 30da2a2ae4..a67dd05e7e 100644 --- a/mkdocs/config/config_options.py +++ b/mkdocs/config/config_options.py @@ -3,6 +3,7 @@ from collections import Sequence import os from collections import namedtuple +import markdown from mkdocs import utils, theme, plugins from mkdocs.config.base import Config, ValidationError @@ -629,7 +630,16 @@ def run_validation(self, value): extensions.append(item) else: raise ValidationError('Invalid Markdown Extensions configuration') - return utils.reduce_list(self.builtins + extensions) + + extensions = utils.reduce_list(self.builtins + extensions) + + # Confirm that Markdown considers extensions to be valid + try: + markdown.Markdown(extensions=extensions, extension_configs=self.configdata) + except Exception as e: + raise ValidationError(e.args[0]) + + return extensions def post_validation(self, config, key_name): config[self.configkey] = self.configdata diff --git a/mkdocs/tests/config/config_options_tests.py b/mkdocs/tests/config/config_options_tests.py index 56b7a7240e..1d573ba244 100644 --- a/mkdocs/tests/config/config_options_tests.py +++ b/mkdocs/tests/config/config_options_tests.py @@ -2,6 +2,7 @@ import os import unittest +from mock import patch import mkdocs from mkdocs import utils @@ -491,7 +492,8 @@ def test_defined(self): class MarkdownExtensionsTest(unittest.TestCase): - def test_simple_list(self): + @patch('markdown.Markdown') + def test_simple_list(self, mockMd): option = config_options.MarkdownExtensions() config = { 'markdown_extensions': ['foo', 'bar'] @@ -503,7 +505,8 @@ def test_simple_list(self): 'mdx_configs': {} }, config) - def test_list_dicts(self): + @patch('markdown.Markdown') + def test_list_dicts(self, mockMd): option = config_options.MarkdownExtensions() config = { 'markdown_extensions': [ @@ -522,7 +525,8 @@ def test_list_dicts(self): } }, config) - def test_mixed_list(self): + @patch('markdown.Markdown') + def test_mixed_list(self, mockMd): option = config_options.MarkdownExtensions() config = { 'markdown_extensions': [ @@ -539,7 +543,8 @@ def test_mixed_list(self): } }, config) - def test_builtins(self): + @patch('markdown.Markdown') + def test_builtins(self, mockMd): option = config_options.MarkdownExtensions(builtins=['meta', 'toc']) config = { 'markdown_extensions': ['foo', 'bar'] @@ -577,7 +582,8 @@ def test_builtins_config(self): 'mdx_configs': {'toc': {'permalink': True}} }, config) - def test_configkey(self): + @patch('markdown.Markdown') + def test_configkey(self, mockMd): option = config_options.MarkdownExtensions(configkey='bar') config = { 'markdown_extensions': [ @@ -605,12 +611,14 @@ def test_none(self): 'mdx_configs': {} }, config) - def test_not_list(self): + @patch('markdown.Markdown') + def test_not_list(self, mockMd): option = config_options.MarkdownExtensions() self.assertRaises(config_options.ValidationError, option.validate, 'not a list') - def test_invalid_config_option(self): + @patch('markdown.Markdown') + def test_invalid_config_option(self, mockMd): option = config_options.MarkdownExtensions() config = { 'markdown_extensions': [ @@ -622,7 +630,8 @@ def test_invalid_config_option(self): option.validate, config['markdown_extensions'] ) - def test_invalid_config_item(self): + @patch('markdown.Markdown') + def test_invalid_config_item(self, mockMd): option = config_options.MarkdownExtensions() config = { 'markdown_extensions': [ @@ -634,7 +643,8 @@ def test_invalid_config_item(self): option.validate, config['markdown_extensions'] ) - def test_invalid_dict_item(self): + @patch('markdown.Markdown') + def test_invalid_dict_item(self, mockMd): option = config_options.MarkdownExtensions() config = { 'markdown_extensions': [ @@ -645,3 +655,13 @@ def test_invalid_dict_item(self): config_options.ValidationError, option.validate, config['markdown_extensions'] ) + + def test_unknown_extension(self): + option = config_options.MarkdownExtensions() + config = { + 'markdown_extensions': ['unknown'] + } + self.assertRaises( + config_options.ValidationError, + option.validate, config['markdown_extensions'] + ) diff --git a/requirements/project-min.txt b/requirements/project-min.txt index 6b0262e0d1..a60bec245a 100644 --- a/requirements/project-min.txt +++ b/requirements/project-min.txt @@ -4,3 +4,4 @@ livereload==2.5.1 Markdown==2.5 PyYAML==3.10 tornado==4.1 +mdx_gh_links>=0.2 From 5c644955e4ffc55e1d7e1c186158b16f5c8d21a6 Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Fri, 6 Apr 2018 14:03:52 -0400 Subject: [PATCH 038/342] Ensure unrelated problems don't cause tests to fail. Avoid calling mkdocs.config.load_config from tests that aren't testing that function. --- mkdocs/tests/cli_tests.py | 18 ++++++++++++------ mkdocs/tests/gh_deploy_tests.py | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/mkdocs/tests/cli_tests.py b/mkdocs/tests/cli_tests.py index 85ab87de2b..57f12845fb 100644 --- a/mkdocs/tests/cli_tests.py +++ b/mkdocs/tests/cli_tests.py @@ -186,8 +186,9 @@ def test_build_defaults(self, mock_build, mock_load_config): logger = logging.getLogger('mkdocs') self.assertEqual(logger.level, logging.INFO) + @mock.patch('mkdocs.config.load_config', autospec=True) @mock.patch('mkdocs.commands.build.build', autospec=True) - def test_build_clean(self, mock_build): + def test_build_clean(self, mock_build, mock_load_config): result = self.runner.invoke( cli.cli, ['build', '--clean'], catch_exceptions=False) @@ -198,8 +199,9 @@ def test_build_clean(self, mock_build): self.assertTrue('dirty' in kwargs) self.assertFalse(kwargs['dirty']) + @mock.patch('mkdocs.config.load_config', autospec=True) @mock.patch('mkdocs.commands.build.build', autospec=True) - def test_build_dirty(self, mock_build): + def test_build_dirty(self, mock_build, mock_load_config): result = self.runner.invoke( cli.cli, ['build', '--dirty'], catch_exceptions=False) @@ -296,8 +298,9 @@ def test_build_site_dir(self, mock_build, mock_load_config): site_dir='custom' ) + @mock.patch('mkdocs.config.load_config', autospec=True) @mock.patch('mkdocs.commands.build.build', autospec=True) - def test_build_verbose(self, mock_build): + def test_build_verbose(self, mock_build, mock_load_config): result = self.runner.invoke( cli.cli, ['build', '--verbose'], catch_exceptions=False) @@ -307,8 +310,9 @@ def test_build_verbose(self, mock_build): logger = logging.getLogger('mkdocs') self.assertEqual(logger.level, logging.DEBUG) + @mock.patch('mkdocs.config.load_config', autospec=True) @mock.patch('mkdocs.commands.build.build', autospec=True) - def test_build_quiet(self, mock_build): + def test_build_quiet(self, mock_build, mock_load_config): result = self.runner.invoke( cli.cli, ['build', '--quiet'], catch_exceptions=False) @@ -352,9 +356,10 @@ def test_gh_deploy_defaults(self, mock_gh_deploy, mock_build, mock_load_config): remote_name=None ) + @mock.patch('mkdocs.config.load_config', autospec=True) @mock.patch('mkdocs.commands.build.build', autospec=True) @mock.patch('mkdocs.commands.gh_deploy.gh_deploy', autospec=True) - def test_gh_deploy_clean(self, mock_gh_deploy, mock_build): + def test_gh_deploy_clean(self, mock_gh_deploy, mock_build, mock_load_config): result = self.runner.invoke( cli.cli, ['gh-deploy', '--clean'], catch_exceptions=False) @@ -366,9 +371,10 @@ def test_gh_deploy_clean(self, mock_gh_deploy, mock_build): self.assertTrue('dirty' in kwargs) self.assertFalse(kwargs['dirty']) + @mock.patch('mkdocs.config.load_config', autospec=True) @mock.patch('mkdocs.commands.build.build', autospec=True) @mock.patch('mkdocs.commands.gh_deploy.gh_deploy', autospec=True) - def test_gh_deploy_dirty(self, mock_gh_deploy, mock_build): + def test_gh_deploy_dirty(self, mock_gh_deploy, mock_build, mock_load_config): result = self.runner.invoke( cli.cli, ['gh-deploy', '--dirty'], catch_exceptions=False) diff --git a/mkdocs/tests/gh_deploy_tests.py b/mkdocs/tests/gh_deploy_tests.py index b08e28df41..d12f98cc71 100644 --- a/mkdocs/tests/gh_deploy_tests.py +++ b/mkdocs/tests/gh_deploy_tests.py @@ -3,7 +3,7 @@ import unittest import mock -from mkdocs.config import load_config +from mkdocs.tests.base import load_config from mkdocs.commands import gh_deploy From cfc688b1a07d483de5348d09fd04ec1a596dc5d2 Mon Sep 17 00:00:00 2001 From: Tien Nguyen Date: Mon, 9 Apr 2018 21:45:42 +0700 Subject: [PATCH 039/342] Update sitemap.xml (#1465) Hide when it nav_item.update_date empty --- mkdocs/templates/sitemap.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mkdocs/templates/sitemap.xml b/mkdocs/templates/sitemap.xml index 68ad772586..fe7ce01c5e 100644 --- a/mkdocs/templates/sitemap.xml +++ b/mkdocs/templates/sitemap.xml @@ -5,14 +5,14 @@ {% for nav_item in nav_item.children %} {{ config.site_url }}{{ nav_item.abs_url }} - {{nav_item.update_date}} + {% if nav_item.update_date %}{{nav_item.update_date}}{% endif %} daily {% endfor %} {% else %} {{ config.site_url }}{{ nav_item.abs_url }} - {{nav_item.update_date}} + {% if nav_item.update_date %}{{nav_item.update_date}}{% endif %} daily {% endif %} From ee37665418a8c1a20eff6041fe17d0d107dde07c Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Fri, 6 Apr 2018 16:03:22 -0400 Subject: [PATCH 040/342] Ensure deploy commit message points at correct sha. If/when deploying to usr/org pages, the deploy script is run from the Pages repo, not the project repo. Therefore, we need ot get the current sha from the project repo. --- mkdocs/commands/gh_deploy.py | 6 +++--- mkdocs/tests/base.py | 2 +- mkdocs/tests/gh_deploy_tests.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mkdocs/commands/gh_deploy.py b/mkdocs/commands/gh_deploy.py index b7b54be713..ccadab6d90 100644 --- a/mkdocs/commands/gh_deploy.py +++ b/mkdocs/commands/gh_deploy.py @@ -18,9 +18,9 @@ def _is_cwd_git_repo(): return proc.wait() == 0 -def _get_current_sha(): +def _get_current_sha(repo_path): - proc = subprocess.Popen(['git', 'rev-parse', '--short', 'HEAD'], + proc = subprocess.Popen(['git', 'rev-parse', '--short', 'HEAD'], cwd=repo_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, _ = proc.communicate() @@ -57,7 +57,7 @@ def gh_deploy(config, message=None, force=False): if message is None: message = default_message - sha = _get_current_sha() + sha = _get_current_sha(os.path.dirname(config.config_file_path)) message = message.format(version=mkdocs.__version__, sha=sha) remote_branch = config['remote_branch'] diff --git a/mkdocs/tests/base.py b/mkdocs/tests/base.py index 51cfee36a8..20ba63ec16 100644 --- a/mkdocs/tests/base.py +++ b/mkdocs/tests/base.py @@ -31,7 +31,7 @@ def load_config(**cfg): if 'docs_dir' not in cfg: # Point to an actual dir to avoid a 'does not exist' error on validation. cfg['docs_dir'] = os.path.join(path_base, 'docs') - conf = config.Config(schema=config.DEFAULT_SCHEMA) + conf = config.Config(schema=config.DEFAULT_SCHEMA, config_file_path=cfg['config_file_path']) conf.load_dict(cfg) errors_warnings = conf.validate() diff --git a/mkdocs/tests/gh_deploy_tests.py b/mkdocs/tests/gh_deploy_tests.py index d12f98cc71..7b8793bc47 100644 --- a/mkdocs/tests/gh_deploy_tests.py +++ b/mkdocs/tests/gh_deploy_tests.py @@ -28,7 +28,7 @@ def test_get_current_sha(self, mock_popeno): mock_popeno().communicate.return_value = (b'6d98394\n', b'') - self.assertEqual(gh_deploy._get_current_sha(), u'6d98394') + self.assertEqual(gh_deploy._get_current_sha('.'), u'6d98394') @mock.patch('subprocess.Popen') def test_get_remote_url_ssh(self, mock_popeno): From e5c2459fdf58e55237fef914ac67a968c68d2a3f Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Thu, 12 Apr 2018 16:08:33 -0400 Subject: [PATCH 041/342] Warn users deploying with older version. (#1467) Fixes #640. --- docs/about/release-notes.md | 1 + mkdocs/__main__.py | 6 +- mkdocs/commands/gh_deploy.py | 39 +++++++++++-- mkdocs/tests/base.py | 99 +++++++++++++++++++++++++++++++++ mkdocs/tests/cli_tests.py | 18 ++++++ mkdocs/tests/gh_deploy_tests.py | 69 ++++++++++++++++++++++- 6 files changed, 225 insertions(+), 7 deletions(-) diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md index 6f0f35185f..7afca02a03 100644 --- a/docs/about/release-notes.md +++ b/docs/about/release-notes.md @@ -66,6 +66,7 @@ authors should review how [search and themes] interact. ### Other Changes and Additions to Development Version +* Add MkDocs version check to gh-deploy script (#640). * Improve Markdown extension error messages. (#782). * Drop official support for Python 3.3 and set `tornado>=5.0` (#1427). * Add support for GitLab edit links (#1435). diff --git a/mkdocs/__main__.py b/mkdocs/__main__.py index 8c231180fe..a3f7cdaebd 100644 --- a/mkdocs/__main__.py +++ b/mkdocs/__main__.py @@ -89,6 +89,7 @@ def common_options(f): remote_name_help = ("The remote name to commit to for Github Pages. This " "overrides the value specified in config") force_help = "Force the push to the repository." +ignore_version_help = "Ignore check that build is not being deployed with an older version of MkDocs." pgk_dir = os.path.dirname(os.path.abspath(__file__)) @@ -172,8 +173,9 @@ def build_command(clean, config_file, strict, theme, theme_dir, site_dir): @click.option('-b', '--remote-branch', help=remote_branch_help) @click.option('-r', '--remote-name', help=remote_name_help) @click.option('--force', is_flag=True, help=force_help) +@click.option('--ignore-version', is_flag=True, help=ignore_version_help) @common_options -def gh_deploy_command(config_file, clean, message, remote_branch, remote_name, force): +def gh_deploy_command(config_file, clean, message, remote_branch, remote_name, force, ignore_version): """Deploy your documentation to GitHub Pages""" try: cfg = config.load_config( @@ -182,7 +184,7 @@ def gh_deploy_command(config_file, clean, message, remote_branch, remote_name, f remote_name=remote_name ) build.build(cfg, dirty=not clean) - gh_deploy.gh_deploy(cfg, message=message, force=force) + gh_deploy.gh_deploy(cfg, message=message, force=force, ignore_version=ignore_version) except exceptions.ConfigurationError as e: # pragma: no cover # Avoid ugly, unhelpful traceback raise SystemExit('\n' + str(e)) diff --git a/mkdocs/commands/gh_deploy.py b/mkdocs/commands/gh_deploy.py index ccadab6d90..b3d0548f27 100644 --- a/mkdocs/commands/gh_deploy.py +++ b/mkdocs/commands/gh_deploy.py @@ -2,6 +2,8 @@ import logging import subprocess import os +import re +from pkg_resources import parse_version import mkdocs from mkdocs.utils import ghp_import @@ -49,20 +51,49 @@ def _get_remote_url(remote_name): return host, path -def gh_deploy(config, message=None, force=False): +def _check_version(branch): + + proc = subprocess.Popen(['git', 'show', '-s', '--format=%s', 'refs/heads/{}'.format(branch)], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + stdout, _ = proc.communicate() + msg = stdout.decode('utf-8').strip() + m = re.search(r'\d+(\.\d+)+', msg, re.X | re.I) + previousv = parse_version(m.group()) if m else None + currentv = parse_version(mkdocs.__version__) + if not previousv: + log.warn('Version check skipped: No version specificed in previous deployment.') + elif currentv > previousv: + log.info( + 'Previous deployment was done with MkDocs version {}; ' + 'you are deploying with a newer version ({})'.format(previousv, currentv) + ) + elif currentv < previousv: + log.error( + 'Deployment terminated: Previous deployment was made with MkDocs version {}; ' + 'you are attempting to deploy with an older version ({}). Use --ignore-version ' + 'to deploy anyway.'.format(previousv, currentv) + ) + raise SystemExit(1) + + +def gh_deploy(config, message=None, force=False, ignore_version=False): if not _is_cwd_git_repo(): log.error('Cannot deploy - this directory does not appear to be a git ' 'repository') + remote_branch = config['remote_branch'] + remote_name = config['remote_name'] + + if not ignore_version: + _check_version(remote_branch) + if message is None: message = default_message sha = _get_current_sha(os.path.dirname(config.config_file_path)) message = message.format(version=mkdocs.__version__, sha=sha) - remote_branch = config['remote_branch'] - remote_name = config['remote_name'] - log.info("Copying '%s' to '%s' branch and pushing to GitHub.", config['site_dir'], config['remote_branch']) diff --git a/mkdocs/tests/base.py b/mkdocs/tests/base.py index 20ba63ec16..a17fc2e072 100644 --- a/mkdocs/tests/base.py +++ b/mkdocs/tests/base.py @@ -2,9 +2,14 @@ import textwrap import markdown import os +import logging +import collections +import unittest + from mkdocs import toc from mkdocs import config +from mkdocs import utils def dedent(text): @@ -37,3 +42,97 @@ def load_config(**cfg): errors_warnings = conf.validate() assert(errors_warnings == ([], [])), errors_warnings return conf + + +# Backport unittest.TestCase.assertLogs for Python 2.7 +# see https://github.com/python/cpython/blob/3.6/Lib/unittest/case.py + +if not utils.PY3: + _LoggingWatcher = collections.namedtuple("_LoggingWatcher", + ["records", "output"]) + + class _CapturingHandler(logging.Handler): + """ + A logging handler capturing all (raw and formatted) logging output. + """ + + def __init__(self): + logging.Handler.__init__(self) + self.watcher = _LoggingWatcher([], []) + + def flush(self): + pass + + def emit(self, record): + self.watcher.records.append(record) + msg = self.format(record) + self.watcher.output.append(msg) + + class _AssertLogsContext(object): + """A context manager used to implement TestCase.assertLogs().""" + + LOGGING_FORMAT = "%(levelname)s:%(name)s:%(message)s" + + def __init__(self, test_case, logger_name, level): + self.test_case = test_case + self.logger_name = logger_name + if level: + self.level = logging._levelNames.get(level, level) + else: + self.level = logging.INFO + self.msg = None + + def __enter__(self): + if isinstance(self.logger_name, logging.Logger): + logger = self.logger = self.logger_name + else: + logger = self.logger = logging.getLogger(self.logger_name) + formatter = logging.Formatter(self.LOGGING_FORMAT) + handler = _CapturingHandler() + handler.setFormatter(formatter) + self.watcher = handler.watcher + self.old_handlers = logger.handlers[:] + self.old_level = logger.level + self.old_propagate = logger.propagate + logger.handlers = [handler] + logger.setLevel(self.level) + logger.propagate = False + return handler.watcher + + def __exit__(self, exc_type, exc_value, tb): + self.logger.handlers = self.old_handlers + self.logger.propagate = self.old_propagate + self.logger.setLevel(self.old_level) + if exc_type is not None: + # let unexpected exceptions pass through + return False + if len(self.watcher.records) == 0: + self._raiseFailure( + "no logs of level {} or higher triggered on {}" + .format(logging.getLevelName(self.level), self.logger.name)) + + def _raiseFailure(self, standardMsg): + msg = self.test_case._formatMessage(self.msg, standardMsg) + raise self.test_case.failureException(msg) + + class LogTestCase(unittest.TestCase): + def assertLogs(self, logger=None, level=None): + """Fail unless a log message of level *level* or higher is emitted + on *logger_name* or its children. If omitted, *level* defaults to + INFO and *logger* defaults to the root logger. + This method must be used as a context manager, and will yield + a recording object with two attributes: `output` and `records`. + At the end of the context manager, the `output` attribute will + be a list of the matching formatted log messages and the + `records` attribute will be a list of the corresponding LogRecord + objects. + Example:: + with self.assertLogs('foo', level='INFO') as cm: + logging.getLogger('foo').info('first message') + logging.getLogger('foo.bar').error('second message') + self.assertEqual(cm.output, ['INFO:foo:first message', + 'ERROR:foo.bar:second message']) + """ + return _AssertLogsContext(self, logger, level) +else: + LogTestCase = unittest.TestCase diff --git a/mkdocs/tests/cli_tests.py b/mkdocs/tests/cli_tests.py index 57f12845fb..939096f743 100644 --- a/mkdocs/tests/cli_tests.py +++ b/mkdocs/tests/cli_tests.py @@ -346,6 +346,8 @@ def test_gh_deploy_defaults(self, mock_gh_deploy, mock_build, mock_load_config): self.assertEqual(g_kwargs['message'], None) self.assertTrue('force' in g_kwargs) self.assertEqual(g_kwargs['force'], False) + self.assertTrue('ignore_version' in g_kwargs) + self.assertEqual(g_kwargs['ignore_version'], False) self.assertEqual(mock_build.call_count, 1) b_args, b_kwargs = mock_build.call_args self.assertTrue('dirty' in b_kwargs) @@ -471,3 +473,19 @@ def test_gh_deploy_force(self, mock_gh_deploy, mock_build, mock_load_config): self.assertEqual(g_kwargs['force'], True) self.assertEqual(mock_build.call_count, 1) self.assertEqual(mock_load_config.call_count, 1) + + @mock.patch('mkdocs.config.load_config', autospec=True) + @mock.patch('mkdocs.commands.build.build', autospec=True) + @mock.patch('mkdocs.commands.gh_deploy.gh_deploy', autospec=True) + def test_gh_deploy_ognore_version(self, mock_gh_deploy, mock_build, mock_load_config): + + result = self.runner.invoke( + cli.cli, ['gh-deploy', '--ignore-version'], catch_exceptions=False) + + self.assertEqual(result.exit_code, 0) + self.assertEqual(mock_gh_deploy.call_count, 1) + g_args, g_kwargs = mock_gh_deploy.call_args + self.assertTrue('ignore_version' in g_kwargs) + self.assertEqual(g_kwargs['ignore_version'], True) + self.assertEqual(mock_build.call_count, 1) + self.assertEqual(mock_load_config.call_count, 1) diff --git a/mkdocs/tests/gh_deploy_tests.py b/mkdocs/tests/gh_deploy_tests.py index 7b8793bc47..495a6e902e 100644 --- a/mkdocs/tests/gh_deploy_tests.py +++ b/mkdocs/tests/gh_deploy_tests.py @@ -3,8 +3,9 @@ import unittest import mock -from mkdocs.tests.base import load_config +from mkdocs.tests.base import load_config, LogTestCase from mkdocs.commands import gh_deploy +from mkdocs import __version__ class TestGitHubDeploy(unittest.TestCase): @@ -99,6 +100,32 @@ def test_deploy_hostname(self, mock_import, get_remote, get_sha, is_repo): ) gh_deploy.gh_deploy(config) + @mock.patch('mkdocs.commands.gh_deploy._is_cwd_git_repo', return_value=True) + @mock.patch('mkdocs.commands.gh_deploy._get_current_sha', return_value='shashas') + @mock.patch('mkdocs.commands.gh_deploy._get_remote_url', return_value=(None, None)) + @mock.patch('mkdocs.commands.gh_deploy._check_version') + @mock.patch('mkdocs.commands.gh_deploy.ghp_import.ghp_import', return_value=(True, '')) + def test_deploy_ignore_version_default(self, mock_import, check_version, get_remote, get_sha, is_repo): + + config = load_config( + remote_branch='test', + ) + gh_deploy.gh_deploy(config) + check_version.assert_called_once() + + @mock.patch('mkdocs.commands.gh_deploy._is_cwd_git_repo', return_value=True) + @mock.patch('mkdocs.commands.gh_deploy._get_current_sha', return_value='shashas') + @mock.patch('mkdocs.commands.gh_deploy._get_remote_url', return_value=(None, None)) + @mock.patch('mkdocs.commands.gh_deploy._check_version') + @mock.patch('mkdocs.commands.gh_deploy.ghp_import.ghp_import', return_value=(True, '')) + def test_deploy_ignore_version(self, mock_import, check_version, get_remote, get_sha, is_repo): + + config = load_config( + remote_branch='test', + ) + gh_deploy.gh_deploy(config, ignore_version=True) + check_version.assert_not_called() + @mock.patch('mkdocs.utils.ghp_import.ghp_import') @mock.patch('mkdocs.commands.gh_deploy.log') def test_deploy_error(self, mock_log, mock_import): @@ -112,3 +139,43 @@ def test_deploy_error(self, mock_log, mock_import): self.assertRaises(SystemExit, gh_deploy.gh_deploy, config) mock_log.error.assert_called_once_with('Failed to deploy to GitHub with error: \n%s', error_string) + + +class TestGitHubDeployLogs(LogTestCase): + + @mock.patch('subprocess.Popen') + def test_mkdocs_newer(self, mock_popeno): + + mock_popeno().communicate.return_value = (b'Deployed 12345678 with MkDocs version: 0.1.2\n', b'') + + with self.assertLogs('mkdocs', level='INFO') as cm: + gh_deploy._check_version('gh-pages') + self.assertEqual( + cm.output, ['INFO:mkdocs.commands.gh_deploy:Previous deployment was done with MkDocs ' + 'version 0.1.2; you are deploying with a newer version ({})'.format(__version__)] + ) + + @mock.patch('subprocess.Popen') + def test_mkdocs_older(self, mock_popeno): + + mock_popeno().communicate.return_value = (b'Deployed 12345678 with MkDocs version: 10.1.2\n', b'') + + with self.assertLogs('mkdocs', level='ERROR') as cm: + self.assertRaises(SystemExit, gh_deploy._check_version, 'gh-pages') + self.assertEqual( + cm.output, ['ERROR:mkdocs.commands.gh_deploy:Deployment terminated: Previous deployment was made with ' + 'MkDocs version 10.1.2; you are attempting to deploy with an older version ({}). Use ' + '--ignore-version to deploy anyway.'.format(__version__)] + ) + + @mock.patch('subprocess.Popen') + def test_version_unknown(self, mock_popeno): + + mock_popeno().communicate.return_value = (b'No version specified\n', b'') + + with self.assertLogs('mkdocs', level='WARNING') as cm: + gh_deploy._check_version('gh-pages') + self.assertEqual( + cm.output, + ['WARNING:mkdocs.commands.gh_deploy:Version check skipped: No version specificed in previous deployment.'] + ) From 7e06ca68d100d27c0323a59a13a9a692c2ee537f Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Wed, 9 May 2018 10:58:23 -0400 Subject: [PATCH 042/342] Add multi-level nesting support to sitemap.xml Fixes #1481. --- docs/about/release-notes.md | 4 ++++ mkdocs/templates/sitemap.xml | 32 ++++++++++++++++---------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md index 7afca02a03..d07ca9062d 100644 --- a/docs/about/release-notes.md +++ b/docs/about/release-notes.md @@ -88,6 +88,10 @@ authors should review how [search and themes] interact. * Update links to Python-Markdown library (#1360). * Document how to generate manpages for MkDocs commands (#686). +## Version 0.17.4 + +* Bugfix: Add multi-level nesting support to sitemap.xml (#1482). + ## Version 0.17.3 (2018-03-07) * Bugfix: Set dependency `tornado>=4.1,<5.0` due to changes in 5.0 (#1428). diff --git a/mkdocs/templates/sitemap.xml b/mkdocs/templates/sitemap.xml index fe7ce01c5e..cac1d1d72a 100644 --- a/mkdocs/templates/sitemap.xml +++ b/mkdocs/templates/sitemap.xml @@ -1,20 +1,20 @@ - - -{% for nav_item in nav %} - {% if nav_item.children %} - {% for nav_item in nav_item.children %} +{%- macro nav_item(item) -%} + {%- if item.children -%} + {%- for child in item.children -%} + {{ nav_item(child) }} + {%- endfor -%} + {%- else %} - {{ config.site_url }}{{ nav_item.abs_url }} - {% if nav_item.update_date %}{{nav_item.update_date}}{% endif %} + {% if item.canonical_url %}{{ item.canonical_url }}{% else %}{{ item.abs_url }}{% endif %} + {% if item.update_date %}{{item.update_date}}{% endif %} daily - {% endfor %} - {% else %} - - {{ config.site_url }}{{ nav_item.abs_url }} - {% if nav_item.update_date %}{{nav_item.update_date}}{% endif %} - daily - - {% endif %} -{% endfor %} + {%- endif -%} +{%- endmacro -%} + + + +{%- for item in nav -%} + {{ nav_item(item) }} +{%- endfor %} From 34a579ec573ed83b293bd42e98fa847876534004 Mon Sep 17 00:00:00 2001 From: Phil Elson Date: Wed, 23 May 2018 08:29:53 +0100 Subject: [PATCH 043/342] Ensure name is provided in warning about deprecation. --- mkdocs/config/config_options.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mkdocs/config/config_options.py b/mkdocs/config/config_options.py index a67dd05e7e..f410f72d5c 100644 --- a/mkdocs/config/config_options.py +++ b/mkdocs/config/config_options.py @@ -171,8 +171,9 @@ def pre_validation(self, config, key_name): if config.get(key_name) is None or self.moved_to is None: return - warning = ('The configuration option {0} has been deprecated and will ' - 'be removed in a future release of MkDocs.') + warning = ('The configuration option {0} has been deprecated and ' + 'will be removed in a future release of MkDocs.' + ''.format(key_name)) self.warnings.append(warning) if '.' not in self.moved_to: From 7dbcf9a22a1c59da1412a409fc6566064b24cfc7 Mon Sep 17 00:00:00 2001 From: Dmitry Shachnev Date: Wed, 23 May 2018 13:07:47 +0300 Subject: [PATCH 044/342] Make test_doc_dir_in_site_dir work with custom build path The previous version worked only when ../mkdocs was pointing back to the current directory. Fixes #1490. --- mkdocs/tests/config/config_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs/tests/config/config_tests.py b/mkdocs/tests/config/config_tests.py index d956bf3b46..68c76c9484 100644 --- a/mkdocs/tests/config/config_tests.py +++ b/mkdocs/tests/config/config_tests.py @@ -239,7 +239,7 @@ def test_doc_dir_in_site_dir(self): {'docs_dir': '.', 'site_dir': '.'}, {'docs_dir': 'docs', 'site_dir': ''}, {'docs_dir': '', 'site_dir': ''}, - {'docs_dir': j('..', 'mkdocs', 'docs'), 'site_dir': 'docs'}, + {'docs_dir': 'docs', 'site_dir': 'docs'}, ) conf = { From 3f0e4464a787ad5ce5a235d7ae988a2d8f53f077 Mon Sep 17 00:00:00 2001 From: Dmitry Shachnev Date: Wed, 23 May 2018 16:47:58 +0300 Subject: [PATCH 045/342] Make the tests pass when git is not installed --- mkdocs/tests/gh_deploy_tests.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/mkdocs/tests/gh_deploy_tests.py b/mkdocs/tests/gh_deploy_tests.py index 495a6e902e..98b337fee5 100644 --- a/mkdocs/tests/gh_deploy_tests.py +++ b/mkdocs/tests/gh_deploy_tests.py @@ -67,8 +67,9 @@ def test_get_remote_url_enterprise(self, mock_popeno): @mock.patch('mkdocs.commands.gh_deploy._is_cwd_git_repo', return_value=True) @mock.patch('mkdocs.commands.gh_deploy._get_current_sha', return_value='shashas') @mock.patch('mkdocs.commands.gh_deploy._get_remote_url', return_value=(None, None)) + @mock.patch('mkdocs.commands.gh_deploy._check_version') @mock.patch('mkdocs.commands.gh_deploy.ghp_import.ghp_import', return_value=(True, '')) - def test_deploy(self, mock_import, get_remote, get_sha, is_repo): + def test_deploy(self, mock_import, check_version, get_remote, get_sha, is_repo): config = load_config( remote_branch='test', @@ -78,9 +79,10 @@ def test_deploy(self, mock_import, get_remote, get_sha, is_repo): @mock.patch('mkdocs.commands.gh_deploy._is_cwd_git_repo', return_value=True) @mock.patch('mkdocs.commands.gh_deploy._get_current_sha', return_value='shashas') @mock.patch('mkdocs.commands.gh_deploy._get_remote_url', return_value=(None, None)) + @mock.patch('mkdocs.commands.gh_deploy._check_version') @mock.patch('mkdocs.commands.gh_deploy.ghp_import.ghp_import', return_value=(True, '')) @mock.patch('os.path.isfile', return_value=False) - def test_deploy_no_cname(self, mock_isfile, mock_import, get_remote, + def test_deploy_no_cname(self, mock_isfile, mock_import, check_version, get_remote, get_sha, is_repo): config = load_config( @@ -92,8 +94,9 @@ def test_deploy_no_cname(self, mock_isfile, mock_import, get_remote, @mock.patch('mkdocs.commands.gh_deploy._get_current_sha', return_value='shashas') @mock.patch('mkdocs.commands.gh_deploy._get_remote_url', return_value=( u'git@', u'mkdocs/mkdocs.git')) + @mock.patch('mkdocs.commands.gh_deploy._check_version') @mock.patch('mkdocs.commands.gh_deploy.ghp_import.ghp_import', return_value=(True, '')) - def test_deploy_hostname(self, mock_import, get_remote, get_sha, is_repo): + def test_deploy_hostname(self, mock_import, check_version, get_remote, get_sha, is_repo): config = load_config( remote_branch='test', @@ -126,9 +129,12 @@ def test_deploy_ignore_version(self, mock_import, check_version, get_remote, get gh_deploy.gh_deploy(config, ignore_version=True) check_version.assert_not_called() + @mock.patch('mkdocs.commands.gh_deploy._is_cwd_git_repo', return_value=True) + @mock.patch('mkdocs.commands.gh_deploy._get_current_sha', return_value='shashas') + @mock.patch('mkdocs.commands.gh_deploy._check_version') @mock.patch('mkdocs.utils.ghp_import.ghp_import') @mock.patch('mkdocs.commands.gh_deploy.log') - def test_deploy_error(self, mock_log, mock_import): + def test_deploy_error(self, mock_log, mock_import, check_version, get_sha, is_repo): error_string = 'TestError123' mock_import.return_value = (False, error_string) From 34ef3ca6d0390959080ce93a695361eea1649272 Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Thu, 28 Jun 2018 15:08:17 -0400 Subject: [PATCH 046/342] Internal Refactor of Pages, Files, and Navigation (#1504) Internal handling of pages, files and navigation has been completely refactored. The changes included in the refactor are summarized below. * Support for hidden pages. All Markdown pages are now included in the build regardless of whether they are included in the navigation configuration (fixes #699). * The navigation can now include links to external sites (fixes #989, fixes #1373, & fixes #1406). * Page data (including titles) is properly determined for all pages before any page is rendered (fixes #1347). * Automatically populated navigation now sorts index pages to the top. In other words, The index page will be listed as the first child of a directory, while all other documents are sorted alphanumerically by file name after the index page (fixes #73 & fixes #1042). * A `README.md` file is now treated as an index file within a directory and will be rendered to `index.html` (fixes #608). * The URLs for all files are computed once and stored in a files collection. This ensures all internal links are always computed correctly regardless of the configuration. This also allows all internal links to be validated, not just links to other Markdown pages. (fixes #842 & fixes #872). * An `on_files` plugin event has been added, which could be used to include files not in the `docs_dir`, exclude files, redefine page URLs (i.e. implement extensionless URLs), or to manipulate files in various other ways. Backward incompatible changes are highlighted in the release notes included with this commit. Some notes regarding various decisions follow in no particular order: This started out as the contents of the 'structure' dir from @tomchristie's work in #689. All paths must be all Unicode all the time. When a byte string and a Unicode string are both passed to os.path (join ect) then returned value is always a byte string. Therefore, we need every path string to be Unicode. This ensures validation checks that and if the byte string uses the file system encoding, decodes it. For any other encoding, a validation error is raised. Paths which start with a slash are assumed to be relative to the docs_dir root. This behavior fixes #192. However, the slash not being present in the output may surprise some users who are trying to create a link relative to the server root when the mkdocs root is not at the server root. The URLs available on a page are: * Page.url is the url relative to the site_dir * Page.canonical_url is the relative url joined with site_url or None if site_url is not defined (the default). * Page.abs_url is the path component of the canonical url or None if canonical_url is None. Note that new behavior is slightly different than before. Previously abs_url ignored site_url and was always url with '' prepended. With the new behavior, if site_url includes a subdir, that subdir will be included in the abs_url. When not on a server, there is no sensable "absolute_url" for a page. Therefore, we shouldn't try to define one. The thinking is that users generating docs to be browsed in the local file system (`file://`) should leave the site_url setting unset, while users who will be serving their docs from a server should be setting the site_url. And if the site_url point sot a subdir of the server, the abs_url will stil be absolute from the server root as it uses the "path" of the canonical_url of the page. Note that without the magical url context all URLs must be prepended by `{{ base_url }}/` in the templates. While this requires a change in third party themes, it is more consistent. Links being ignored in raw HTML is now documented. Fixes #991. All related tests that require temp dirs use the `mkdocs.tests.base.tempdir` decorator. Note that any unrelated tests have not yet been updated. That can happen separately from this. The one test in `mkdocs.tests.structure.page_tests` (test_BOM) is unique enough to not use the decorator. --- .coveragerc | 2 + docs/about/release-notes.md | 195 +++- docs/user-guide/configuration.md | 74 +- docs/user-guide/custom-themes.md | 246 ++++- docs/user-guide/deploying-your-docs.md | 4 +- docs/user-guide/plugins.md | 24 +- docs/user-guide/styling-your-docs.md | 13 +- docs/user-guide/writing-your-docs.md | 108 ++- mkdocs.yml | 2 +- mkdocs/commands/build.py | 322 +++---- mkdocs/config/base.py | 8 + mkdocs/config/config_options.py | 89 +- mkdocs/config/defaults.py | 7 +- mkdocs/contrib/search/search_index.py | 6 +- mkdocs/nav.py | 416 -------- mkdocs/plugins.py | 2 +- mkdocs/structure/__init__.py | 0 mkdocs/structure/files.py | 266 ++++++ mkdocs/structure/nav.py | 182 ++++ mkdocs/structure/pages.py | 266 ++++++ mkdocs/structure/toc.py | 131 +++ mkdocs/tests/base.py | 103 +- mkdocs/tests/build_tests.py | 890 +++++++++--------- mkdocs/tests/config/base_tests.py | 4 +- mkdocs/tests/config/config_options_tests.py | 106 ++- mkdocs/tests/config/config_tests.py | 81 +- .../integration/complicated_config/mkdocs.yml | 2 +- mkdocs/tests/integration/minimal/mkdocs.yml | 2 +- .../tests/integration/subpages/docs/index.md | 2 +- .../integration/subpages/docs/metadata.md | 5 + .../integration/subpages/docs/page-title.md | 1 + .../integration/subpages/docs/pageTitle.md | 1 + .../unicode/docs/\303\234bersicht.md" | 2 +- .../integration/unicode/docs/\342\231\252.md" | 2 +- mkdocs/tests/nav_tests.py | 850 ----------------- mkdocs/tests/search_tests.py | 21 +- mkdocs/tests/structure/__init__.py | 0 mkdocs/tests/structure/file_tests.py | 576 ++++++++++++ mkdocs/tests/structure/nav_tests.py | 343 +++++++ mkdocs/tests/structure/page_tests.py | 789 ++++++++++++++++ mkdocs/tests/{ => structure}/toc_tests.py | 66 +- mkdocs/tests/utils/utils_tests.py | 160 ++-- mkdocs/themes/mkdocs/nav-sub.html | 2 +- mkdocs/themes/mkdocs/nav.html | 8 +- mkdocs/themes/readthedocs/base.html | 6 +- mkdocs/themes/readthedocs/breadcrumbs.html | 2 +- mkdocs/themes/readthedocs/footer.html | 4 +- mkdocs/themes/readthedocs/nav.html | 2 +- mkdocs/themes/readthedocs/versions.html | 4 +- mkdocs/utils/__init__.py | 29 +- setup.py | 2 +- 51 files changed, 4180 insertions(+), 2248 deletions(-) create mode 100644 .coveragerc delete mode 100644 mkdocs/nav.py create mode 100644 mkdocs/structure/__init__.py create mode 100644 mkdocs/structure/files.py create mode 100644 mkdocs/structure/nav.py create mode 100644 mkdocs/structure/pages.py create mode 100644 mkdocs/structure/toc.py create mode 100644 mkdocs/tests/integration/subpages/docs/metadata.md create mode 100644 mkdocs/tests/integration/subpages/docs/page-title.md create mode 100644 mkdocs/tests/integration/subpages/docs/pageTitle.md delete mode 100644 mkdocs/tests/nav_tests.py create mode 100644 mkdocs/tests/structure/__init__.py create mode 100644 mkdocs/tests/structure/file_tests.py create mode 100644 mkdocs/tests/structure/nav_tests.py create mode 100644 mkdocs/tests/structure/page_tests.py rename mkdocs/tests/{ => structure}/toc_tests.py (63%) diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000000..013dd2021d --- /dev/null +++ b/.coveragerc @@ -0,0 +1,2 @@ +[report] +show_missing = True diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md index d07ca9062d..91a2472d87 100644 --- a/docs/about/release-notes.md +++ b/docs/about/release-notes.md @@ -25,6 +25,131 @@ The current and past members of the MkDocs team. ### Major Additions to Development Version +#### Internal Refactor of Pages, Files, and Navigation + +Internal handling of pages, files and navigation has been completely refactored. +The changes included in the refactor are summarized below. + +* Support for hidden pages. All Markdown pages are now included in the build + regardless of whether they are included in the navigation configuration + (#699). +* The navigation can now include links to external sites (#989 #1373 & #1406). +* Page data (including titles) is properly determined for all pages before any + page is rendered (#1347). +* Automatically populated navigation now sorts index pages to the top. In other + words, The index page will be listed as the first child of a directory, while + all other documents are sorted alphanumerically by file name after the index + page (#73 & #1042). +* A `README.md` file is now treated as an index file within a directory and + will be rendered to `index.html` (#608). +* The URLs for all files are computed once and stored in a files collection. + This ensures all internal links are always computed correctly regardless of + the configuration. This also allows all internal links to be validated, not + just links to other Markdown pages. (#842 & #872). +* An [on_files] plugin event has been added, which could be used to include + files not in the `docs_dir`, exclude files, redefine page URLs (i.e. + implement extensionless URLs), or to manipulate files in various other ways. + + [on_files]: ../user-guide/plugins.md#on_files + +##### Backward Incompatible Changes + +As part of the internal refactor, a number of backward incompatible changes have +been introduced, which are summarized below. + +###### URLS have changed when `use_directory_urls` is `False` + +Previously, all Markdown pages would be have their filenames altered to be index +pages regardless of how the [use_directory_urls] setting was configured. +However, the path munging is only needed when `use_directory_urls` is set to +`True` (the default). The path mungling no longer happens when +`use_directory_urls` is set to `False`, which will result in different URLs for +all pages that were not already index files. As this behavior only effects a +non-default configuration, and the most common user-case for setting the option +to `False` is for local file system (`file://`) browsing, its not likely to +effect most users. However, if you have `use_directory_urls` set to `False` +for a MkDocs site hosted on a web server, most of your URLs will now be broken. +As you can see below, the new URLs are much more sensible. + +| Markdown file | Old URL | New URL | +| --------------- | -------------------- | -------------- | +| `index.md` | `index.html` | `index.html` | +| `foo.md` | `foo/index.html` | `foo.html` | +| `foo/bar.md` | `foo/bar/index.html` | `foo/bar.html` | + +Note that there has been no change to URLs or file paths when +`use_directory_urls` is set to `True` (the default), except that MkDocs more +consistently includes an ending slash on all internally generated URLs. + +[use_directory_urls]: ../user-guide/configuration.md#use_directory_urls + +###### The `pages` configuration setting has been renamed to `nav` + +The `pages` configuration setting is deprecated and will issue a warning if set +in the configuration file. The setting has been renamed `nav`. To update your +configuration, simply rename the setting to `nav`. In other words, if your +configuration looked like this: + +```yaml +pages: + - Home: index.md + - User Guide: user-guide.md +``` + +Simply edit the configuration as follows: + +```yaml +nav: + - Home: index.md + - User Guide: user-guide.md +``` + +In the current release, any configuration which includes a `pages` setting, but +no `nav` setting, the `pages` configuration will be copied to `nav` and a +warning will be issued. However, in a future release, that may no longer happen. +If both `pages` and `nav` are defined, the `pages` setting will be ignored. + +###### Template variables and `base_url` + +In previous versions of MkDocs some URLs expected the [base_url] template +variable to be prepended to the URL and others did not. That inconsistency has +been removed. All URLs must now be joined with the `base_url`. As previously, a +slash must be included between the `base_url` and the URL variable. For example, +a theme template might have previously included a link to the `site_name` as: + +```django +{{ config.site_name }} +``` + +And MkDocs would magically return a URL for the homepage which was relative to +the current page. That "magic" has been removed and the `base_url` must now be +explicitly included: + +```django +{{ config.site_name }} +``` + +This change applies to any navigation items and pages, as well as the +`page.next_page` and `page.previous_page` attributes. For the time being, the +`extra_javascript` and `extra_css` variables continue to work as previously +(without `base_url`), but they have been deprecated and the corresponding +configuration values (`config.extra_javascript` and `config.extra_css` +respectively) should be used with `base_url` instead. + +Note that navigation can now include links to external sites. Obviously, the +`base_url` should not be prepended to these items. Therefore, all navigation +items contain a `is_link` attribute which can be used to alter the behavior for +external links. + +```django +{{ nav_item.title }} +``` + +Any other URL variables which should not be used with `base_url` are explicitly +documented as such. + +[base_url]: ../user-guide/custom-themes.md#base_url + #### Path Based Settings are Relative to Configuration File (#543) Previously any relative paths in the various configuration options were @@ -181,7 +306,7 @@ template exists. ##### Context Variables Page specific variable names in the template context have been refactored as -defined in [Custom Themes](../user-guide/custom-themes/#page). The +defined in [Custom Themes](../user-guide/custom-themes.md#page). The old variable names issued a warning in version 0.16, but have been removed in version 1.0. @@ -199,14 +324,14 @@ user created and third-party templates: | previous_page | [page.previous_page]| | next_page | [page.next_page] | -[page]: ../user-guide/custom-themes/#page -[page.title]: ../user-guide/custom-themes/#pagetitle -[page.content]: ../user-guide/custom-themes/#pagecontent -[page.toc]: ../user-guide/custom-themes/#pagetoc -[page.meta]: ../user-guide/custom-themes/#pagemeta -[page.canonical_url]: ../user-guide/custom-themes/#pagecanonical_url -[page.previous_page]: ../user-guide/custom-themes/#pageprevious_page -[page.next_page]: ../user-guide/custom-themes/#pagenext_page +[page]: ../user-guide/custom-themes.md#page +[page.title]: ../user-guide/custom-themes.md#pagetitle +[page.content]: ../user-guide/custom-themes.md#pagecontent +[page.toc]: ../user-guide/custom-themes.md#pagetoc +[page.meta]: ../user-guide/custom-themes.md#pagemeta +[page.canonical_url]: ../user-guide/custom-themes.md#pagecanonical_url +[page.previous_page]: ../user-guide/custom-themes.md#pageprevious_page +[page.next_page]: ../user-guide/custom-themes.md#pagenext_page Additionally, a number of global variables have been altered and/or removed and user created and third-party templates should be updated as outlined below: @@ -286,7 +411,7 @@ the `extra_css` or `extra_javascript` config settings going forward. ##### Page Context Page specific variable names in the template context have been refactored as -defined in [Custom Themes](../user-guide/custom-themes/#page). The +defined in [Custom Themes](../user-guide/custom-themes.md#page). The old variable names will issue a warning but continue to work for version 0.16, but may be removed in a future version. @@ -304,14 +429,14 @@ user created and third-party templates: | previous_page | [page.previous_page]| | next_page | [page.next_page] | -[page]: ../user-guide/custom-themes/#page -[page.title]: ../user-guide/custom-themes/#pagetitle -[page.content]: ../user-guide/custom-themes/#pagecontent -[page.toc]: ../user-guide/custom-themes/#pagetoc -[page.meta]: ../user-guide/custom-themes/#pagemeta -[page.canonical_url]: ../user-guide/custom-themes/#pagecanonical_url -[page.previous_page]: ../user-guide/custom-themes/#pageprevious_page -[page.next_page]: ../user-guide/custom-themes/#pagenext_page +[page]: ../user-guide/custom-themes.md#page +[page.title]: ../user-guide/custom-themes.md#pagetitle +[page.content]: ../user-guide/custom-themes.md#pagecontent +[page.toc]: ../user-guide/custom-themes.md#pagetoc +[page.meta]: ../user-guide/custom-themes.md#pagemeta +[page.canonical_url]: ../user-guide/custom-themes.md#pagecanonical_url +[page.previous_page]: ../user-guide/custom-themes.md#pageprevious_page +[page.next_page]: ../user-guide/custom-themes.md#pagenext_page ##### Global Context @@ -400,7 +525,7 @@ overriding blocks in the same manner as the built-in themes. Third party themes are encouraged to wrap the various pieces of their templates in blocks in order to support such customization. -[blocks]: ../user-guide/styling-your-docs/#overriding-template-blocks +[blocks]: ../user-guide/styling-your-docs.md#overriding-template-blocks #### Auto-Populated `extra_css` and `extra_javascript` Deprecated. (#986) @@ -444,7 +569,7 @@ the `docs_dir` is set to the directory which contains your config file rather than a child directory. You will need to rearrange you directory structure to better conform with the documented [layout]. -[layout]: ../user-guide/writing-your-docs/#file-layout +[layout]: ../user-guide/writing-your-docs.md#file-layout ### Other Changes and Additions to Version 0.16.0 @@ -522,8 +647,8 @@ See the documentation for [Styling your docs] for more information about using and customizing themes and [Custom themes] for creating and distributing new themes -[Styling your docs]: /user-guide/styling-your-docs.md -[Custom themes]: /user-guide/custom-themes.md +[Styling your docs]: ../user-guide/styling-your-docs.md +[Custom themes]: ../user-guide/custom-themes.md ### Other Changes and Additions to Version 0.15.0 @@ -544,9 +669,9 @@ themes * Bugfix: Provide filename to Read the Docs. (#721 and RTD#1480) * Bugfix: Silence Click's unicode_literals warning. (#708) -[site_description]: /user-guide/configuration.md#site_description -[site_author]: /user-guide/configuration.md#site_author -[ReadTheDocs]: /user-guide/styling-your-docs.md#readthedocs +[site_description]: ../user-guide/configuration.md#site_description +[site_author]: ../user-guide/configuration.md#site_author +[ReadTheDocs]: ../user-guide/styling-your-docs.md#readthedocs ## Version 0.14.0 (2015-06-09) @@ -604,7 +729,7 @@ This new file is created on every MkDocs build (with `mkdocs build`) and no configuration is needed to enable it. [future release]: https://github.com/mkdocs/mkdocs/pull/481 -[site_dir]: /user-guide/configuration.md#site_dir +[site_dir]: ../user-guide/configuration.md#site_dir #### Change the pages configuration @@ -612,8 +737,8 @@ Provide a [new way] to define pages, and specifically [nested pages], in the mkdocs.yml file and deprecate the existing approach, support will be removed with MkDocs 1.0. -[new way]: /user-guide/writing-your-docs.md#configure-pages-and-navigation -[nested pages]: /user-guide/writing-your-docs.md#multilevel-documentation +[new way]: ../user-guide/writing-your-docs.md#configure-pages-and-navigation +[nested pages]: ../user-guide/writing-your-docs.md#multilevel-documentation #### Warn users about the removal of builtin themes @@ -631,7 +756,7 @@ JavaScript library [lunr.js]. It has been added to both the `mkdocs` and for adding it to your own themes. [lunr.js]: http://lunrjs.com/ -[supporting search]: /user-guide/styling-your-docs.md#search-and-themes +[supporting search]: ../user-guide/styling-your-docs.md#search-and-themes #### New Command Line Interface @@ -659,10 +784,10 @@ can also use Jinja2 syntax and take advantage of the [global variables]. By default MkDocs will use this approach to create a sitemap for the documentation. -[extra_javascript]: /user-guide/configuration.md#extra_javascript -[extra_css]: /user-guide/configuration.md#extra_css -[extra_templates]: /user-guide/configuration.md#extra_templates -[global variables]: /user-guide/styling-your-docs.md#global-context +[extra_javascript]: ../user-guide/configuration.md#extra_javascript +[extra_css]: ../user-guide/configuration.md#extra_css +[extra_templates]: ../user-guide/configuration.md#extra_templates +[global variables]: ../user-guide/styling-your-docs.md#global-context ### Other Changes and Additions to Version 0.13.0 @@ -679,8 +804,8 @@ documentation. called index.md (#535) * Bugfix: Fix errors with Unicode filenames (#542). -[extra config]: /user-guide/configuration.md#extra -[Markdown extension configuration options]: /user-guide/configuration.md#markdown_extensions +[extra config]: ../user-guide/configuration.md#extra +[Markdown extension configuration options]: ../user-guide/configuration.md#markdown_extensions [wheels]: http://pythonwheels.com/ ## Version 0.12.2 (2015-04-22) diff --git a/docs/user-guide/configuration.md b/docs/user-guide/configuration.md index 8cac62f917..c9a16b30af 100644 --- a/docs/user-guide/configuration.md +++ b/docs/user-guide/configuration.md @@ -162,24 +162,60 @@ This option can be overridden by a command line option in `gh-deploy`. ## Documentation layout -### pages +### nav -This setting is used to determine the set of pages that should be built for the -documentation. For example, the following would create Introduction, User Guide -and About pages, given the three source files `index.md`, `user-guide.md` and -`about.md`, respectively. +This setting is used to determine the format and layout of the global navigation +for the site. For example, the following would create "Introduction", "User +Guide" and "About" navigation items. ```yaml -pages: +nav: - 'Introduction': 'index.md' - 'User Guide': 'user-guide.md' - 'About': 'about.md' ``` -See the section on [configuring pages and navigation] for a more detailed -breakdown, including how to create sub-sections. +All paths must be relative to the `mkdocs.yml` configuration file. See the +section on [configuring pages and navigation] for a more detailed breakdown, +including how to create sub-sections. -**default**: By default `pages` will contain an alphanumerically sorted, nested +Navigation items may also include links to external sites. While titles are +optional for internal links, they are required for external links. An external +link may be a full URL or a relative URL. Any path which is not found in the +files is assumed to be an external link. + +```yaml +nav: + - Home: index.md + - User Guide: user-guide.md + - Bug Tracker: https://example.com/ +``` + +In the above example, the first two items point to local files while the third +points to an external site. + +However, sometimes the MkDocs site is hosted in a subdirectory of a project's +site and you may want to link to other parts of the same site without including +the full domain. In that case, you may use and appropriate relative URL. + +```yaml +site_url: http://example.com/foo/ + +nav: + - Home: ../ + - User Guide: user-guide.md + - Bug Tracker: /bugs/ +``` + +In the above example, two different styles of external links are used. First +note that the `site_url` indicates that the MkDocs site is hosted in the `/foo/` +subdirectory of the domain. Therefore, the `Home` navigation item is a relative +link which steps up one level to the server root and effectively points to +`http://example.com/`. The `Bug Tracker` item uses an absolute path from the +server root and effectively points to `http://example.com/bugs/`. Of course, the +`User Guide` points to a local MkDocs page. + +**default**: By default `nav` will contain an alphanumerically sorted, nested list of all the Markdown files found within the `docs_dir` and its sub-directories. If none are found it will be `[]` (an empty list). @@ -324,27 +360,26 @@ documentation. The following table demonstrates how the URLs used on the site differ when setting `use_directory_urls` to `true` or `false`. -Source file | Generated HTML | use_directory_urls: true | use_directory_urls: false ------------- | -------------------- | ------------------------ | ------------------------ -index.md | index.html | / | /index.html -api-guide.md | api-guide/index.html | /api-guide/ | /api-guide/index.html -about.md | about/index.html | /about/ | /about/index.html +Source file | use_directory_urls: true | use_directory_urls: false +---------------- | ------------------------- | ------------------------- +index.md | / | /index.html +api-guide.md | /api-guide/ | /api-guide.html +about/license.md | /about/license/ | /about/license.html The default style of `use_directory_urls: true` creates more user friendly URLs, and is usually what you'll want to use. The alternate style can occasionally be useful if you want your documentation to remain properly linked when opening pages directly from the file system, because -it create links that point directly to the target *file* rather than the target +it creates links that point directly to the target *file* rather than the target *directory*. **default**: `true` ### strict -Determines if a broken link to a page within the documentation is considered a -warning or an error (link to a page not listed in the pages setting). Set to -true to halt processing when a broken link is found, false prints a warning. +Determines how warnings are handled. Set to `true` to halt processing when a +warning is raised. Set to `false` to print a warning and continue processing. **default**: `false` @@ -519,7 +554,7 @@ You may [contribute additional languages]. any reason, a warning is issued. You may use the `--strict` flag when building to cause such a failure to raise an error instead. - !!! Note +!!! Note On smaller sites, using a pre-built index is not recommended as it creates a significant increase is bandwidth requirements with little to no noticeable @@ -545,3 +580,4 @@ You may [contribute additional languages]. [ISO 639-1]: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes [Lunr Languages]: https://github.com/MihaiValentin/lunr-languages#lunr-languages----- [contribute additional languages]: https://github.com/MihaiValentin/lunr-languages/blob/master/CONTRIBUTING.md +[Node.js]: https://nodejs.org/ diff --git a/docs/user-guide/custom-themes.md b/docs/user-guide/custom-themes.md index 18f72b282a..dc1c971f3e 100644 --- a/docs/user-guide/custom-themes.md +++ b/docs/user-guide/custom-themes.md @@ -126,6 +126,8 @@ used options include: * [config.site_url](./configuration.md#site_url) * [config.site_author](./configuration.md#site_author) * [config.site_description](./configuration.md#site_description) +* [config.extra_javascript](./configuration.md#extra_javascript) +* [config.extra_css](./configuration.md#extra_css) * [config.repo_url](./configuration.md#repo_url) * [config.repo_name](./configuration.md#repo_name) * [config.copyright](./configuration.md#copyright) @@ -133,7 +135,29 @@ used options include: #### nav -The `nav` variable is used to create the navigation for the documentation. +The `nav` variable is used to create the navigation for the documentation. The +`nav` object is an iterable of [navigation objects](#navigation-objects) as +defined by the [nav] configuration setting. + +[nav]: configuration.md#nav + +In addition to the iterable of [navigation objects](#navigation-objects), the +`nav` object contains the following attributes: + +##### nav.homepage + +The [page](#page) object for the homepage of the site. + +##### nav.pages + +A flat list of all [page](#page) objects contained in the navigation. This list +is not necessarily a complete list of all site pages as it does not contain +pages which are not included in the navigation. This list does match the list +and order of pages used for all "next page" and "previous page" links. For a +list of all pages, use the [pages](#pages) template variable. + +##### Nav Example + Following is a basic usage example which outputs the first and second level navigation as a nested list. @@ -145,15 +169,15 @@ navigation as a nested list.

    @@ -36,7 +36,7 @@ {%- else %}
  • - {{ nav_item.title }} + {{ nav_item.title }}
  • {%- endif %} {%- endfor %} @@ -58,12 +58,12 @@ {%- block next_prev %} {%- if page and (page.next_page or page.previous_page) %}
  • -
  • -
  • diff --git a/mkdocs/themes/readthedocs/base.html b/mkdocs/themes/readthedocs/base.html index c80d7d9fe7..b19c3c37e1 100644 --- a/mkdocs/themes/readthedocs/base.html +++ b/mkdocs/themes/readthedocs/base.html @@ -32,7 +32,7 @@ {% endif %} @@ -66,7 +66,7 @@