From 0bb2dcc319ecb3221f345f2fe97aa12d8b6c698b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 24 Oct 2023 17:23:46 +0000 Subject: [PATCH] Deploy v1.8.0 from 0962591 --- latest | 2 +- v1.8.0/.buildinfo | 4 + v1.8.0/_images/readme-banner.png | Bin 0 -> 64939 bytes v1.8.0/_modules/index.html | 462 + v1.8.0/_modules/pooch.html | 525 + v1.8.0/_modules/pooch/core.html | 1278 ++ v1.8.0/_modules/pooch/downloaders.html | 1598 +++ v1.8.0/_modules/pooch/hashes.html | 678 + v1.8.0/_modules/pooch/processors.html | 827 ++ v1.8.0/_modules/pooch/utils.html | 803 ++ ...-main.c949a650a448cc0ae9fd3441c0e17fb0.css | 1 + ...ables.87249bd3f7b54630a2508c1b61607c7f.css | 7 + v1.8.0/_sources/about.rst.txt | 56 + .../api/generated/pooch.DOIDownloader.rst.txt | 142 + .../api/generated/pooch.Decompress.rst.txt | 154 + .../api/generated/pooch.FTPDownloader.rst.txt | 142 + .../generated/pooch.HTTPDownloader.rst.txt | 142 + .../api/generated/pooch.Pooch.rst.txt | 206 + .../generated/pooch.SFTPDownloader.rst.txt | 142 + .../api/generated/pooch.Untar.rst.txt | 150 + .../api/generated/pooch.Unzip.rst.txt | 154 + .../api/generated/pooch.check_version.rst.txt | 10 + .../api/generated/pooch.create.rst.txt | 10 + .../api/generated/pooch.file_hash.rst.txt | 10 + .../api/generated/pooch.get_logger.rst.txt | 10 + .../api/generated/pooch.make_registry.rst.txt | 10 + .../api/generated/pooch.os_cache.rst.txt | 10 + .../api/generated/pooch.retrieve.rst.txt | 10 + .../_sources/api/generated/pooch.test.rst.txt | 10 + v1.8.0/_sources/api/index.rst.txt | 66 + v1.8.0/_sources/authentication.rst.txt | 82 + v1.8.0/_sources/changes.rst.txt | 856 ++ v1.8.0/_sources/citing.rst.txt | 3 + v1.8.0/_sources/compatibility.rst.txt | 71 + v1.8.0/_sources/decompressing.rst.txt | 56 + v1.8.0/_sources/downloaders.rst.txt | 126 + v1.8.0/_sources/hashes.rst.txt | 131 + v1.8.0/_sources/index.rst.txt | 128 + v1.8.0/_sources/install.rst.txt | 67 + v1.8.0/_sources/logging.rst.txt | 27 + v1.8.0/_sources/multiple-files.rst.txt | 121 + v1.8.0/_sources/multiple-urls.rst.txt | 82 + v1.8.0/_sources/processors.rst.txt | 134 + v1.8.0/_sources/progressbars.rst.txt | 133 + v1.8.0/_sources/protocols.rst.txt | 126 + v1.8.0/_sources/registry-files.rst.txt | 178 + v1.8.0/_sources/retrieve.rst.txt | 101 + v1.8.0/_sources/sample-data.rst.txt | 212 + v1.8.0/_sources/unpacking.rst.txt | 76 + v1.8.0/_sources/user-defined-cache.rst.txt | 34 + v1.8.0/_sources/versions.rst.txt | 35 + v1.8.0/_static/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 213 bytes v1.8.0/_static/banner.svg | 237 + v1.8.0/_static/basic.css | 906 ++ v1.8.0/_static/css/blank.css | 2 + ...index.ff1ffe594081f20da1ef19478df9384b.css | 6 + v1.8.0/_static/css/theme.css | 120 + v1.8.0/_static/doctools.js | 326 + v1.8.0/_static/documentation_options.js | 12 + v1.8.0/_static/favicon.png | Bin 0 -> 1700 bytes v1.8.0/_static/file.png | Bin 0 -> 286 bytes v1.8.0/_static/images/logo_binder.svg | 19 + v1.8.0/_static/images/logo_colab.png | Bin 0 -> 7601 bytes v1.8.0/_static/images/logo_deepnote.svg | 1 + v1.8.0/_static/images/logo_jupyterhub.svg | 1 + v1.8.0/_static/jquery-3.5.1.js | 10872 ++++++++++++++++ v1.8.0/_static/jquery.js | 2 + .../_static/js/index.be7d3bbb2ef33a8344ce.js | 32 + v1.8.0/_static/language_data.js | 297 + v1.8.0/_static/minus.png | Bin 0 -> 90 bytes ...-main.c949a650a448cc0ae9fd3441c0e17fb0.css | 1 + ...ables.87249bd3f7b54630a2508c1b61607c7f.css | 7 + v1.8.0/_static/plus.png | Bin 0 -> 90 bytes v1.8.0/_static/pooch-logo.png | Bin 0 -> 27120 bytes v1.8.0/_static/pooch-logo.svg | 197 + v1.8.0/_static/pygments.css | 75 + v1.8.0/_static/readme-banner.png | Bin 0 -> 64939 bytes v1.8.0/_static/readme-banner.svg | 253 + v1.8.0/_static/searchtools.js | 529 + ...-theme.9d8b4a8b9bb19db25eeaddc40d639ba2.js | 20 + v1.8.0/_static/sphinx-book-theme.css | 5 + v1.8.0/_static/style.css | 35 + v1.8.0/_static/underscore-1.13.1.js | 2042 +++ v1.8.0/_static/underscore.js | 6 + .../vendor/fontawesome/5.13.0/LICENSE.txt | 34 + .../vendor/fontawesome/5.13.0/css/all.min.css | 5 + .../5.13.0/webfonts/fa-brands-400.eot | Bin 0 -> 133034 bytes .../5.13.0/webfonts/fa-brands-400.svg | 3570 +++++ .../5.13.0/webfonts/fa-brands-400.ttf | Bin 0 -> 132728 bytes .../5.13.0/webfonts/fa-brands-400.woff | Bin 0 -> 89824 bytes .../5.13.0/webfonts/fa-brands-400.woff2 | Bin 0 -> 76612 bytes .../5.13.0/webfonts/fa-regular-400.eot | Bin 0 -> 34390 bytes .../5.13.0/webfonts/fa-regular-400.svg | 803 ++ .../5.13.0/webfonts/fa-regular-400.ttf | Bin 0 -> 34092 bytes .../5.13.0/webfonts/fa-regular-400.woff | Bin 0 -> 16800 bytes .../5.13.0/webfonts/fa-regular-400.woff2 | Bin 0 -> 13584 bytes .../5.13.0/webfonts/fa-solid-900.eot | Bin 0 -> 202902 bytes .../5.13.0/webfonts/fa-solid-900.svg | 4938 +++++++ .../5.13.0/webfonts/fa-solid-900.ttf | Bin 0 -> 202616 bytes .../5.13.0/webfonts/fa-solid-900.woff | Bin 0 -> 103300 bytes .../5.13.0/webfonts/fa-solid-900.woff2 | Bin 0 -> 79444 bytes v1.8.0/_static/webpack-macros.html | 25 + v1.8.0/about.html | 581 + v1.8.0/api/generated/pooch.DOIDownloader.html | 599 + v1.8.0/api/generated/pooch.Decompress.html | 574 + v1.8.0/api/generated/pooch.FTPDownloader.html | 559 + .../api/generated/pooch.HTTPDownloader.html | 602 + v1.8.0/api/generated/pooch.Pooch.html | 668 + .../api/generated/pooch.SFTPDownloader.html | 553 + v1.8.0/api/generated/pooch.Untar.html | 557 + v1.8.0/api/generated/pooch.Unzip.html | 557 + v1.8.0/api/generated/pooch.check_version.html | 529 + v1.8.0/api/generated/pooch.create.html | 631 + v1.8.0/api/generated/pooch.file_hash.html | 521 + v1.8.0/api/generated/pooch.get_logger.html | 507 + v1.8.0/api/generated/pooch.make_registry.html | 512 + v1.8.0/api/generated/pooch.os_cache.html | 518 + v1.8.0/api/generated/pooch.retrieve.html | 655 + v1.8.0/api/generated/pooch.test.html | 515 + v1.8.0/api/index.html | 670 + v1.8.0/authentication.html | 598 + v1.8.0/changes.html | 1525 +++ v1.8.0/citing.html | 520 + v1.8.0/compatibility.html | 610 + v1.8.0/decompressing.html | 540 + v1.8.0/downloaders.html | 639 + v1.8.0/genindex.html | 654 + v1.8.0/hashes.html | 657 + v1.8.0/index.html | 626 + v1.8.0/install.html | 589 + v1.8.0/logging.html | 538 + v1.8.0/multiple-files.html | 652 + v1.8.0/multiple-urls.html | 587 + v1.8.0/objects.inv | Bin 0 -> 1592 bytes v1.8.0/processors.html | 639 + v1.8.0/progressbars.html | 649 + v1.8.0/protocols.html | 626 + v1.8.0/py-modindex.html | 479 + v1.8.0/registry-files.html | 710 + v1.8.0/retrieve.html | 645 + v1.8.0/sample-data.html | 756 ++ v1.8.0/search.html | 490 + v1.8.0/searchindex.js | 1 + v1.8.0/unpacking.html | 555 + v1.8.0/user-defined-cache.html | 517 + v1.8.0/versions.html | 517 + 147 files changed, 61402 insertions(+), 1 deletion(-) create mode 100644 v1.8.0/.buildinfo create mode 100644 v1.8.0/_images/readme-banner.png create mode 100644 v1.8.0/_modules/index.html create mode 100644 v1.8.0/_modules/pooch.html create mode 100644 v1.8.0/_modules/pooch/core.html create mode 100644 v1.8.0/_modules/pooch/downloaders.html create mode 100644 v1.8.0/_modules/pooch/hashes.html create mode 100644 v1.8.0/_modules/pooch/processors.html create mode 100644 v1.8.0/_modules/pooch/utils.html create mode 100644 v1.8.0/_panels_static/panels-main.c949a650a448cc0ae9fd3441c0e17fb0.css create mode 100644 v1.8.0/_panels_static/panels-variables.87249bd3f7b54630a2508c1b61607c7f.css create mode 100644 v1.8.0/_sources/about.rst.txt create mode 100644 v1.8.0/_sources/api/generated/pooch.DOIDownloader.rst.txt create mode 100644 v1.8.0/_sources/api/generated/pooch.Decompress.rst.txt create mode 100644 v1.8.0/_sources/api/generated/pooch.FTPDownloader.rst.txt create mode 100644 v1.8.0/_sources/api/generated/pooch.HTTPDownloader.rst.txt create mode 100644 v1.8.0/_sources/api/generated/pooch.Pooch.rst.txt create mode 100644 v1.8.0/_sources/api/generated/pooch.SFTPDownloader.rst.txt create mode 100644 v1.8.0/_sources/api/generated/pooch.Untar.rst.txt create mode 100644 v1.8.0/_sources/api/generated/pooch.Unzip.rst.txt create mode 100644 v1.8.0/_sources/api/generated/pooch.check_version.rst.txt create mode 100644 v1.8.0/_sources/api/generated/pooch.create.rst.txt create mode 100644 v1.8.0/_sources/api/generated/pooch.file_hash.rst.txt create mode 100644 v1.8.0/_sources/api/generated/pooch.get_logger.rst.txt create mode 100644 v1.8.0/_sources/api/generated/pooch.make_registry.rst.txt create mode 100644 v1.8.0/_sources/api/generated/pooch.os_cache.rst.txt create mode 100644 v1.8.0/_sources/api/generated/pooch.retrieve.rst.txt create mode 100644 v1.8.0/_sources/api/generated/pooch.test.rst.txt create mode 100644 v1.8.0/_sources/api/index.rst.txt create mode 100644 v1.8.0/_sources/authentication.rst.txt create mode 100644 v1.8.0/_sources/changes.rst.txt create mode 100644 v1.8.0/_sources/citing.rst.txt create mode 100644 v1.8.0/_sources/compatibility.rst.txt create mode 100644 v1.8.0/_sources/decompressing.rst.txt create mode 100644 v1.8.0/_sources/downloaders.rst.txt create mode 100644 v1.8.0/_sources/hashes.rst.txt create mode 100644 v1.8.0/_sources/index.rst.txt create mode 100644 v1.8.0/_sources/install.rst.txt create mode 100644 v1.8.0/_sources/logging.rst.txt create mode 100644 v1.8.0/_sources/multiple-files.rst.txt create mode 100644 v1.8.0/_sources/multiple-urls.rst.txt create mode 100644 v1.8.0/_sources/processors.rst.txt create mode 100644 v1.8.0/_sources/progressbars.rst.txt create mode 100644 v1.8.0/_sources/protocols.rst.txt create mode 100644 v1.8.0/_sources/registry-files.rst.txt create mode 100644 v1.8.0/_sources/retrieve.rst.txt create mode 100644 v1.8.0/_sources/sample-data.rst.txt create mode 100644 v1.8.0/_sources/unpacking.rst.txt create mode 100644 v1.8.0/_sources/user-defined-cache.rst.txt create mode 100644 v1.8.0/_sources/versions.rst.txt create mode 100644 v1.8.0/_static/__init__.py create mode 100644 v1.8.0/_static/__pycache__/__init__.cpython-311.pyc create mode 100644 v1.8.0/_static/banner.svg create mode 100644 v1.8.0/_static/basic.css create mode 100644 v1.8.0/_static/css/blank.css create mode 100644 v1.8.0/_static/css/index.ff1ffe594081f20da1ef19478df9384b.css create mode 100644 v1.8.0/_static/css/theme.css create mode 100644 v1.8.0/_static/doctools.js create mode 100644 v1.8.0/_static/documentation_options.js create mode 100644 v1.8.0/_static/favicon.png create mode 100644 v1.8.0/_static/file.png create mode 100644 v1.8.0/_static/images/logo_binder.svg create mode 100644 v1.8.0/_static/images/logo_colab.png create mode 100644 v1.8.0/_static/images/logo_deepnote.svg create mode 100644 v1.8.0/_static/images/logo_jupyterhub.svg create mode 100644 v1.8.0/_static/jquery-3.5.1.js create mode 100644 v1.8.0/_static/jquery.js create mode 100644 v1.8.0/_static/js/index.be7d3bbb2ef33a8344ce.js create mode 100644 v1.8.0/_static/language_data.js create mode 100644 v1.8.0/_static/minus.png create mode 100644 v1.8.0/_static/panels-main.c949a650a448cc0ae9fd3441c0e17fb0.css create mode 100644 v1.8.0/_static/panels-variables.87249bd3f7b54630a2508c1b61607c7f.css create mode 100644 v1.8.0/_static/plus.png create mode 100644 v1.8.0/_static/pooch-logo.png create mode 100644 v1.8.0/_static/pooch-logo.svg create mode 100644 v1.8.0/_static/pygments.css create mode 100644 v1.8.0/_static/readme-banner.png create mode 100644 v1.8.0/_static/readme-banner.svg create mode 100644 v1.8.0/_static/searchtools.js create mode 100644 v1.8.0/_static/sphinx-book-theme.9d8b4a8b9bb19db25eeaddc40d639ba2.js create mode 100644 v1.8.0/_static/sphinx-book-theme.css create mode 100644 v1.8.0/_static/style.css create mode 100644 v1.8.0/_static/underscore-1.13.1.js create mode 100644 v1.8.0/_static/underscore.js create mode 100644 v1.8.0/_static/vendor/fontawesome/5.13.0/LICENSE.txt create mode 100644 v1.8.0/_static/vendor/fontawesome/5.13.0/css/all.min.css create mode 100644 v1.8.0/_static/vendor/fontawesome/5.13.0/webfonts/fa-brands-400.eot create mode 100644 v1.8.0/_static/vendor/fontawesome/5.13.0/webfonts/fa-brands-400.svg create mode 100644 v1.8.0/_static/vendor/fontawesome/5.13.0/webfonts/fa-brands-400.ttf create mode 100644 v1.8.0/_static/vendor/fontawesome/5.13.0/webfonts/fa-brands-400.woff create mode 100644 v1.8.0/_static/vendor/fontawesome/5.13.0/webfonts/fa-brands-400.woff2 create mode 100644 v1.8.0/_static/vendor/fontawesome/5.13.0/webfonts/fa-regular-400.eot create mode 100644 v1.8.0/_static/vendor/fontawesome/5.13.0/webfonts/fa-regular-400.svg create mode 100644 v1.8.0/_static/vendor/fontawesome/5.13.0/webfonts/fa-regular-400.ttf create mode 100644 v1.8.0/_static/vendor/fontawesome/5.13.0/webfonts/fa-regular-400.woff create mode 100644 v1.8.0/_static/vendor/fontawesome/5.13.0/webfonts/fa-regular-400.woff2 create mode 100644 v1.8.0/_static/vendor/fontawesome/5.13.0/webfonts/fa-solid-900.eot create mode 100644 v1.8.0/_static/vendor/fontawesome/5.13.0/webfonts/fa-solid-900.svg create mode 100644 v1.8.0/_static/vendor/fontawesome/5.13.0/webfonts/fa-solid-900.ttf create mode 100644 v1.8.0/_static/vendor/fontawesome/5.13.0/webfonts/fa-solid-900.woff create mode 100644 v1.8.0/_static/vendor/fontawesome/5.13.0/webfonts/fa-solid-900.woff2 create mode 100644 v1.8.0/_static/webpack-macros.html create mode 100644 v1.8.0/about.html create mode 100644 v1.8.0/api/generated/pooch.DOIDownloader.html create mode 100644 v1.8.0/api/generated/pooch.Decompress.html create mode 100644 v1.8.0/api/generated/pooch.FTPDownloader.html create mode 100644 v1.8.0/api/generated/pooch.HTTPDownloader.html create mode 100644 v1.8.0/api/generated/pooch.Pooch.html create mode 100644 v1.8.0/api/generated/pooch.SFTPDownloader.html create mode 100644 v1.8.0/api/generated/pooch.Untar.html create mode 100644 v1.8.0/api/generated/pooch.Unzip.html create mode 100644 v1.8.0/api/generated/pooch.check_version.html create mode 100644 v1.8.0/api/generated/pooch.create.html create mode 100644 v1.8.0/api/generated/pooch.file_hash.html create mode 100644 v1.8.0/api/generated/pooch.get_logger.html create mode 100644 v1.8.0/api/generated/pooch.make_registry.html create mode 100644 v1.8.0/api/generated/pooch.os_cache.html create mode 100644 v1.8.0/api/generated/pooch.retrieve.html create mode 100644 v1.8.0/api/generated/pooch.test.html create mode 100644 v1.8.0/api/index.html create mode 100644 v1.8.0/authentication.html create mode 100644 v1.8.0/changes.html create mode 100644 v1.8.0/citing.html create mode 100644 v1.8.0/compatibility.html create mode 100644 v1.8.0/decompressing.html create mode 100644 v1.8.0/downloaders.html create mode 100644 v1.8.0/genindex.html create mode 100644 v1.8.0/hashes.html create mode 100644 v1.8.0/index.html create mode 100644 v1.8.0/install.html create mode 100644 v1.8.0/logging.html create mode 100644 v1.8.0/multiple-files.html create mode 100644 v1.8.0/multiple-urls.html create mode 100644 v1.8.0/objects.inv create mode 100644 v1.8.0/processors.html create mode 100644 v1.8.0/progressbars.html create mode 100644 v1.8.0/protocols.html create mode 100644 v1.8.0/py-modindex.html create mode 100644 v1.8.0/registry-files.html create mode 100644 v1.8.0/retrieve.html create mode 100644 v1.8.0/sample-data.html create mode 100644 v1.8.0/search.html create mode 100644 v1.8.0/searchindex.js create mode 100644 v1.8.0/unpacking.html create mode 100644 v1.8.0/user-defined-cache.html create mode 100644 v1.8.0/versions.html diff --git a/latest b/latest index b7c8e167..4e2cea3b 120000 --- a/latest +++ b/latest @@ -1 +1 @@ -v1.7.0 \ No newline at end of file +v1.8.0 \ No newline at end of file diff --git a/v1.8.0/.buildinfo b/v1.8.0/.buildinfo new file mode 100644 index 00000000..588e3591 --- /dev/null +++ b/v1.8.0/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 20556be955bf26861405f9589bb7c5cb +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/v1.8.0/_images/readme-banner.png b/v1.8.0/_images/readme-banner.png new file mode 100644 index 0000000000000000000000000000000000000000..cc959120a1f1a008fec158691e69469dbc092f11 GIT binary patch literal 64939 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 B(2xKC literal 0 HcmV?d00001 diff --git a/v1.8.0/_modules/index.html b/v1.8.0/_modules/index.html new file mode 100644 index 00000000..e66b1814 --- /dev/null +++ b/v1.8.0/_modules/index.html @@ -0,0 +1,462 @@ + + + + + + + + + + Overview: module code | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

+ +
+
+ +
+
+
+ +
+ +

All modules for which code is available

+ + +
+ + + +
+
+ +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/_modules/pooch.html b/v1.8.0/_modules/pooch.html new file mode 100644 index 00000000..f5d9fe97 --- /dev/null +++ b/v1.8.0/_modules/pooch.html @@ -0,0 +1,525 @@ + + + + + + + + + + pooch | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

+ +
+
+ +
+
+
+ +
+ +

Source code for pooch

+# Copyright (c) 2018 The Pooch Developers.
+# Distributed under the terms of the BSD 3-Clause License.
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# This code is part of the Fatiando a Terra project (https://www.fatiando.org)
+#
+# pylint: disable=missing-docstring,import-outside-toplevel,import-self
+#
+# Import functions/classes to make the API
+from .core import Pooch, create, retrieve
+from .utils import os_cache, check_version, get_logger
+from .hashes import file_hash, make_registry
+from .downloaders import (
+    HTTPDownloader,
+    FTPDownloader,
+    SFTPDownloader,
+    DOIDownloader,
+)
+from .processors import Unzip, Untar, Decompress
+
+# This file is generated automatically by setuptools_scm
+from . import _version
+
+
+# Add a "v" to the version number
+__version__ = f"v{_version.version}"
+
+
+
[docs]def test(doctest=True, verbose=True, coverage=False): + """ + Run the test suite. + + Uses `py.test <http://pytest.org/>`__ to discover and run the tests. + + Parameters + ---------- + + doctest : bool + If ``True``, will run the doctests as well (code examples that start + with a ``>>>`` in the docs). + verbose : bool + If ``True``, will print extra information during the test run. + coverage : bool + If ``True``, will run test coverage analysis on the code as well. + Requires ``pytest-cov``. + + Raises + ------ + + AssertionError + If pytest returns a non-zero error code indicating that some tests have + failed. + + """ + import pytest + + package = __name__ + args = [] + if verbose: + args.append("-vv") + if coverage: + args.append(f"--cov={package}") + args.append("--cov-report=term-missing") + if doctest: + args.append("--doctest-modules") + args.append("--pyargs") + args.append(package) + status = pytest.main(args) + assert status == 0, "Some tests have failed."
+
+ +
+ + + +
+
+ +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/_modules/pooch/core.html b/v1.8.0/_modules/pooch/core.html new file mode 100644 index 00000000..ffddde5e --- /dev/null +++ b/v1.8.0/_modules/pooch/core.html @@ -0,0 +1,1278 @@ + + + + + + + + + + pooch.core | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

+ +
+
+ +
+
+
+ +
+ +

Source code for pooch.core

+# Copyright (c) 2018 The Pooch Developers.
+# Distributed under the terms of the BSD 3-Clause License.
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# This code is part of the Fatiando a Terra project (https://www.fatiando.org)
+#
+"""
+The main Pooch class and a factory function for it.
+"""
+import os
+import time
+import contextlib
+from pathlib import Path
+import shlex
+import shutil
+
+
+from .hashes import hash_matches, file_hash
+from .utils import (
+    check_version,
+    get_logger,
+    make_local_storage,
+    cache_location,
+    temporary_file,
+    os_cache,
+    unique_file_name,
+)
+from .downloaders import DOIDownloader, choose_downloader, doi_to_repository
+
+
+
[docs]def retrieve( + url, + known_hash, + fname=None, + path=None, + processor=None, + downloader=None, + progressbar=False, +): + """ + Download and cache a single file locally. + + Uses HTTP or FTP by default, depending on the protocol in the given *url*. + Other download methods can be controlled through the *downloader* argument + (see below). + + The file will be downloaded to a temporary location first and its hash will + be compared to the given *known_hash*. This is done to ensure that the + download happened correctly and securely. If the hash doesn't match, the + file will be deleted and an exception will be raised. + + If the file already exists locally, its hash will be compared to + *known_hash*. If they are not the same, this is interpreted as the file + needing to be updated and it will be downloaded again. + + You can bypass these checks by passing ``known_hash=None``. If this is + done, the SHA256 hash of the downloaded file will be logged to the screen. + It is highly recommended that you copy and paste this hash as *known_hash* + so that future downloads are guaranteed to be the exact same file. This is + crucial for reproducible computations. + + If the file exists in the given *path* with the given *fname* and the hash + matches, it will not be downloaded and the absolute path to the file will + be returned. + + .. note:: + + This function is meant for downloading single files. If you need to + manage the download and caching of several files, with versioning, use + :func:`pooch.create` and :class:`pooch.Pooch` instead. + + Parameters + ---------- + url : str + The URL to the file that is to be downloaded. Ideally, the URL should + end in a file name. + known_hash : str or None + A known hash (checksum) of the file. Will be used to verify the + download or check if an existing file needs to be updated. By default, + will assume it's a SHA256 hash. To specify a different hashing method, + prepend the hash with ``algorithm:``, for example + ``md5:pw9co2iun29juoh`` or ``sha1:092odwhi2ujdp2du2od2odh2wod2``. If + None, will NOT check the hash of the downloaded file or check if an + existing file needs to be updated. + fname : str or None + The name that will be used to save the file. Should NOT include the + full path, just the file name (it will be appended to *path*). If + None, will create a unique file name using a combination of the last + part of the URL (assuming it's the file name) and the MD5 hash of the + URL. For example, ``81whdo2d2e928yd1wi22-data-file.csv``. This ensures + that files from different URLs never overwrite each other, even if they + have the same name. + path : str or PathLike or None + The location of the cache folder on disk. This is where the file will + be saved. If None, will save to a ``pooch`` folder in the default cache + location for your operating system (see :func:`pooch.os_cache`). + processor : None or callable + If not None, then a function (or callable object) that will be called + before returning the full path and after the file has been downloaded + (if required). See :ref:`processors` for details. + downloader : None or callable + If not None, then a function (or callable object) that will be called + to download a given URL to a provided local file name. See + :ref:`downloaders` for details. + progressbar : bool or an arbitrary progress bar object + If True, will print a progress bar of the download to standard error + (stderr). Requires `tqdm <https://github.com/tqdm/tqdm>`__ to be + installed. Alternatively, an arbitrary progress bar object can be + passed. See :ref:`custom-progressbar` for details. + + Returns + ------- + full_path : str + The absolute path (including the file name) of the file in the local + storage. + + Examples + -------- + + Download one of the data files from the Pooch repository on GitHub: + + >>> import os + >>> from pooch import __version__, check_version, retrieve + >>> # Make a URL for the version of pooch we have installed + >>> url = "https://github.com/fatiando/pooch/raw/{}/data/tiny-data.txt" + >>> url = url.format(check_version(__version__, fallback="main")) + >>> # Download the file and save it locally. Will check the MD5 checksum of + >>> # the downloaded file against the given value to make sure it's the + >>> # right file. You can use other hashes by specifying different + >>> # algorithm names (sha256, sha1, etc). + >>> fname = retrieve( + ... url, known_hash="md5:70e2afd3fd7e336ae478b1e740a5f08e", + ... ) + >>> with open(fname) as f: + ... print(f.read().strip()) + # A tiny data file for test purposes only + 1 2 3 4 5 6 + >>> # Running again won't trigger a download and only return the path to + >>> # the existing file. + >>> fname2 = retrieve( + ... url, known_hash="md5:70e2afd3fd7e336ae478b1e740a5f08e", + ... ) + >>> print(fname2 == fname) + True + >>> os.remove(fname) + + Files that are compressed with gzip, xz/lzma, or bzip2 can be automatically + decompressed by passing using the :class:`pooch.Decompress` processor: + + >>> from pooch import Decompress + >>> # URLs to a gzip compressed version of the data file. + >>> url = ("https://github.com/fatiando/pooch/raw/{}/" + ... + "pooch/tests/data/tiny-data.txt.gz") + >>> url = url.format(check_version(__version__, fallback="main")) + >>> # By default, you would have to decompress the file yourself + >>> fname = retrieve( + ... url, + ... known_hash="md5:8812ba10b6c7778014fdae81b03f9def", + ... ) + >>> print(os.path.splitext(fname)[1]) + .gz + >>> # Use the processor to decompress after download automatically and + >>> # return the path to the decompressed file instead. + >>> fname2 = retrieve( + ... url, + ... known_hash="md5:8812ba10b6c7778014fdae81b03f9def", + ... processor=Decompress(), + ... ) + >>> print(fname2 == fname) + False + >>> with open(fname2) as f: + ... print(f.read().strip()) + # A tiny data file for test purposes only + 1 2 3 4 5 6 + >>> os.remove(fname) + >>> os.remove(fname2) + + When downloading archives (zip or tar), it can be useful to unpack them + after download to avoid having to do that yourself. Use the processors + :class:`pooch.Unzip` or :class:`pooch.Untar` to do this automatically: + + >>> from pooch import Unzip + >>> # URLs to a zip archive with a single data file. + >>> url = ("https://github.com/fatiando/pooch/raw/{}/" + ... + "pooch/tests/data/tiny-data.zip") + >>> url = url.format(check_version(__version__, fallback="main")) + >>> # By default, you would get the path to the archive + >>> fname = retrieve( + ... url, + ... known_hash="md5:e9592cb46cf3514a1079051f8a148148", + ... ) + >>> print(os.path.splitext(fname)[1]) + .zip + >>> os.remove(fname) + >>> # Using the processor, the archive will be unzipped and a list with the + >>> # path to every file will be returned instead of a single path. + >>> fnames = retrieve( + ... url, + ... known_hash="md5:e9592cb46cf3514a1079051f8a148148", + ... processor=Unzip(), + ... ) + >>> # There was only a single file in our archive. + >>> print(len(fnames)) + 1 + >>> with open(fnames[0]) as f: + ... print(f.read().strip()) + # A tiny data file for test purposes only + 1 2 3 4 5 6 + >>> for f in fnames: + ... os.remove(f) + + + """ + if path is None: + path = os_cache("pooch") + if fname is None: + fname = unique_file_name(url) + # Make the path absolute. + path = cache_location(path, env=None, version=None) + + full_path = path.resolve() / fname + action, verb = download_action(full_path, known_hash) + + if action in ("download", "update"): + # We need to write data, so create the local data directory if it + # doesn't already exist. + make_local_storage(path) + + get_logger().info( + "%s data from '%s' to file '%s'.", + verb, + url, + str(full_path), + ) + + if downloader is None: + downloader = choose_downloader(url, progressbar=progressbar) + + stream_download(url, full_path, known_hash, downloader, pooch=None) + + if known_hash is None: + get_logger().info( + "SHA256 hash of downloaded file: %s\n" + "Use this value as the 'known_hash' argument of 'pooch.retrieve'" + " to ensure that the file hasn't changed if it is downloaded again" + " in the future.", + file_hash(str(full_path)), + ) + + if processor is not None: + return processor(str(full_path), action, None) + + return str(full_path)
+ + +
[docs]def create( + path, + base_url, + version=None, + version_dev="master", + env=None, + registry=None, + urls=None, + retry_if_failed=0, + allow_updates=True, +): + """ + Create a :class:`~pooch.Pooch` with sensible defaults to fetch data files. + + If a version string is given, the Pooch will be versioned, meaning that the + local storage folder and the base URL depend on the project version. This + is necessary if your users have multiple versions of your library installed + (using virtual environments) and you updated the data files between + versions. Otherwise, every time a user switches environments would trigger + a re-download of the data. The version string will be appended to the local + storage path (for example, ``~/.mypooch/cache/v0.1``) and inserted into the + base URL (for example, + ``https://github.com/fatiando/pooch/raw/v0.1/data``). If the version string + contains ``+XX.XXXXX``, it will be interpreted as a development version. + + Does **not** create the local data storage folder. The folder will only be + created the first time a download is attempted with + :meth:`pooch.Pooch.fetch`. This makes it safe to use this function at the + module level (so it's executed on ``import`` and the resulting + :class:`~pooch.Pooch` is a global variable). + + Parameters + ---------- + path : str, PathLike, list or tuple + The path to the local data storage folder. If this is a list or tuple, + we'll join the parts with the appropriate separator. The *version* will + be appended to the end of this path. Use :func:`pooch.os_cache` for a + sensible default. + base_url : str + Base URL for the remote data source. All requests will be made relative + to this URL. The string should have a ``{version}`` formatting mark in + it. We will call ``.format(version=version)`` on this string. If the + URL does not end in a ``'/'``, a trailing ``'/'`` will be added + automatically. + version : str or None + The version string for your project. Should be PEP440 compatible. If + None is given, will not attempt to format *base_url* and no subfolder + will be appended to *path*. + version_dev : str + The name used for the development version of a project. If your data is + hosted on Github (and *base_url* is a Github raw link), then + ``"master"`` is a good choice (default). Ignored if *version* is None. + env : str or None + An environment variable that can be used to overwrite *path*. This + allows users to control where they want the data to be stored. We'll + append *version* to the end of this value as well. + registry : dict or None + A record of the files that are managed by this Pooch. Keys should be + the file names and the values should be their hashes. Only files + in the registry can be fetched from the local storage. Files in + subdirectories of *path* **must use Unix-style separators** (``'/'``) + even on Windows. + urls : dict or None + Custom URLs for downloading individual files in the registry. A + dictionary with the file names as keys and the custom URLs as values. + Not all files in *registry* need an entry in *urls*. If a file has an + entry in *urls*, the *base_url* will be ignored when downloading it in + favor of ``urls[fname]``. + retry_if_failed : int + Retry a file download the specified number of times if it fails because + of a bad connection or a hash mismatch. By default, downloads are only + attempted once (``retry_if_failed=0``). Initially, will wait for 1s + between retries and then increase the wait time by 1s with each retry + until a maximum of 10s. + allow_updates : bool or str + Whether existing files in local storage that have a hash mismatch with + the registry are allowed to update from the remote URL. If a string is + passed, we will assume it's the name of an environment variable that + will be checked for the true/false value. If ``False``, any mismatch + with hashes in the registry will result in an error. Defaults to + ``True``. + + Returns + ------- + pooch : :class:`~pooch.Pooch` + The :class:`~pooch.Pooch` initialized with the given arguments. + + Examples + -------- + + Create a :class:`~pooch.Pooch` for a release (v0.1): + + >>> pup = create(path="myproject", + ... base_url="http://some.link.com/{version}/", + ... version="v0.1", + ... registry={"data.txt": "9081wo2eb2gc0u..."}) + >>> print(pup.path.parts) # The path is a pathlib.Path + ('myproject', 'v0.1') + >>> # The local folder is only created when a dataset is first downloaded + >>> print(pup.path.exists()) + False + >>> print(pup.base_url) + http://some.link.com/v0.1/ + >>> print(pup.registry) + {'data.txt': '9081wo2eb2gc0u...'} + >>> print(pup.registry_files) + ['data.txt'] + + If this is a development version (12 commits ahead of v0.1), then the + ``version_dev`` will be used (defaults to ``"master"``): + + >>> pup = create(path="myproject", + ... base_url="http://some.link.com/{version}/", + ... version="v0.1+12.do9iwd") + >>> print(pup.path.parts) + ('myproject', 'master') + >>> print(pup.base_url) + http://some.link.com/master/ + + Versioning is optional (but highly encouraged): + + >>> pup = create(path="myproject", + ... base_url="http://some.link.com/", + ... registry={"data.txt": "9081wo2eb2gc0u..."}) + >>> print(pup.path.parts) # The path is a pathlib.Path + ('myproject',) + >>> print(pup.base_url) + http://some.link.com/ + + To place the storage folder at a subdirectory, pass in a list and we'll + join the path for you using the appropriate separator for your operating + system: + + >>> pup = create(path=["myproject", "cache", "data"], + ... base_url="http://some.link.com/{version}/", + ... version="v0.1") + >>> print(pup.path.parts) + ('myproject', 'cache', 'data', 'v0.1') + + The user can overwrite the storage path by setting an environment variable: + + >>> # The variable is not set so we'll use *path* + >>> pup = create(path=["myproject", "not_from_env"], + ... base_url="http://some.link.com/{version}/", + ... version="v0.1", + ... env="MYPROJECT_DATA_DIR") + >>> print(pup.path.parts) + ('myproject', 'not_from_env', 'v0.1') + >>> # Set the environment variable and try again + >>> import os + >>> os.environ["MYPROJECT_DATA_DIR"] = os.path.join("myproject", "env") + >>> pup = create(path=["myproject", "not_env"], + ... base_url="http://some.link.com/{version}/", + ... version="v0.1", + ... env="MYPROJECT_DATA_DIR") + >>> print(pup.path.parts) + ('myproject', 'env', 'v0.1') + + """ + if version is not None: + version = check_version(version, fallback=version_dev) + base_url = base_url.format(version=version) + # Don't create the cache folder here! This function is usually called in + # the module context (at import time), so touching the file system is not + # recommended. It could cause crashes when multiple processes/threads try + # to import at the same time (which would try to create the folder several + # times at once). + path = cache_location(path, env, version) + if isinstance(allow_updates, str): + allow_updates = os.environ.get(allow_updates, "true").lower() != "false" + # add trailing "/" + base_url = base_url.rstrip("/") + "/" + pup = Pooch( + path=path, + base_url=base_url, + registry=registry, + urls=urls, + retry_if_failed=retry_if_failed, + allow_updates=allow_updates, + ) + return pup
+ + +
[docs]class Pooch: + """ + Manager for a local data storage that can fetch from a remote source. + + Avoid creating ``Pooch`` instances directly. Use :func:`pooch.create` + instead. + + Parameters + ---------- + path : str + The path to the local data storage folder. The path must exist in the + file system. + base_url : str + Base URL for the remote data source. All requests will be made relative + to this URL. + registry : dict or None + A record of the files that are managed by this good boy. Keys should be + the file names and the values should be their hashes. Only files + in the registry can be fetched from the local storage. Files in + subdirectories of *path* **must use Unix-style separators** (``'/'``) + even on Windows. + urls : dict or None + Custom URLs for downloading individual files in the registry. A + dictionary with the file names as keys and the custom URLs as values. + Not all files in *registry* need an entry in *urls*. If a file has an + entry in *urls*, the *base_url* will be ignored when downloading it in + favor of ``urls[fname]``. + retry_if_failed : int + Retry a file download the specified number of times if it fails because + of a bad connection or a hash mismatch. By default, downloads are only + attempted once (``retry_if_failed=0``). Initially, will wait for 1s + between retries and then increase the wait time by 1s with each retry + until a maximum of 10s. + allow_updates : bool + Whether existing files in local storage that have a hash mismatch with + the registry are allowed to update from the remote URL. If ``False``, + any mismatch with hashes in the registry will result in an error. + Defaults to ``True``. + + """ + + def __init__( + self, + path, + base_url, + registry=None, + urls=None, + retry_if_failed=0, + allow_updates=True, + ): + self.path = path + self.base_url = base_url + if registry is None: + registry = dict() + self.registry = registry + if urls is None: + urls = dict() + self.urls = dict(urls) + self.retry_if_failed = retry_if_failed + self.allow_updates = allow_updates + + @property + def abspath(self): + "Absolute path to the local storage" + return Path(os.path.abspath(os.path.expanduser(str(self.path)))) + + @property + def registry_files(self): + "List of file names on the registry" + return list(self.registry) + +
[docs] def fetch(self, fname, processor=None, downloader=None, progressbar=False): + """ + Get the absolute path to a file in the local storage. + + If it's not in the local storage, it will be downloaded. If the hash of + the file in local storage doesn't match the one in the registry, will + download a new copy of the file. This is considered a sign that the + file was updated in the remote storage. If the hash of the downloaded + file still doesn't match the one in the registry, will raise an + exception to warn of possible file corruption. + + Post-processing actions sometimes need to be taken on downloaded files + (unzipping, conversion to a more efficient format, etc). If these + actions are time or memory consuming, it would be best to do this only + once right after the file is downloaded. Use the *processor* argument + to specify a function that is executed after the download to perform + these actions. See :ref:`processors` for details. + + Custom file downloaders can be provided through the *downloader* + argument. By default, Pooch will determine the download protocol from + the URL in the registry. If the server for a given file requires + authentication (username and password), use a downloader that support + these features. Downloaders can also be used to print custom messages + (like a progress bar), etc. See :ref:`downloaders` for details. + + Parameters + ---------- + fname : str + The file name (relative to the *base_url* of the remote data + storage) to fetch from the local storage. + processor : None or callable + If not None, then a function (or callable object) that will be + called before returning the full path and after the file has been + downloaded. See :ref:`processors` for details. + downloader : None or callable + If not None, then a function (or callable object) that will be + called to download a given URL to a provided local file name. See + :ref:`downloaders` for details. + progressbar : bool or an arbitrary progress bar object + If True, will print a progress bar of the download to standard + error (stderr). Requires `tqdm <https://github.com/tqdm/tqdm>`__ to + be installed. Alternatively, an arbitrary progress bar object can + be passed. See :ref:`custom-progressbar` for details. + + Returns + ------- + full_path : str + The absolute path (including the file name) of the file in the + local storage. + + """ + self._assert_file_in_registry(fname) + + url = self.get_url(fname) + full_path = self.abspath / fname + known_hash = self.registry[fname] + action, verb = download_action(full_path, known_hash) + + if action == "update" and not self.allow_updates: + raise ValueError( + f"{fname} needs to update {full_path} but updates are disallowed." + ) + + if action in ("download", "update"): + # We need to write data, so create the local data directory if it + # doesn't already exist. + make_local_storage(str(self.abspath)) + + get_logger().info( + "%s file '%s' from '%s' to '%s'.", + verb, + fname, + url, + str(self.abspath), + ) + + if downloader is None: + downloader = choose_downloader(url, progressbar=progressbar) + + stream_download( + url, + full_path, + known_hash, + downloader, + pooch=self, + retry_if_failed=self.retry_if_failed, + ) + + if processor is not None: + return processor(str(full_path), action, self) + + return str(full_path)
+ + def _assert_file_in_registry(self, fname): + """ + Check if a file is in the registry and raise :class:`ValueError` if + it's not. + """ + if fname not in self.registry: + raise ValueError(f"File '{fname}' is not in the registry.") + +
[docs] def get_url(self, fname): + """ + Get the full URL to download a file in the registry. + + Parameters + ---------- + fname : str + The file name (relative to the *base_url* of the remote data + storage) to fetch from the local storage. + + """ + self._assert_file_in_registry(fname) + return self.urls.get(fname, "".join([self.base_url, fname]))
+ +
[docs] def load_registry(self, fname): + """ + Load entries from a file and add them to the registry. + + Use this if you are managing many files. + + Each line of the file should have file name and its hash separated by + a space. Hash can specify checksum algorithm using "alg:hash" format. + In case no algorithm is provided, SHA256 is used by default. + Only one file per line is allowed. Custom download URLs for individual + files can be specified as a third element on the line. Line comments + can be added and must be prepended with ``#``. + + Parameters + ---------- + fname : str | fileobj + Path (or open file object) to the registry file. + + """ + with contextlib.ExitStack() as stack: + if hasattr(fname, "read"): + # It's a file object + fin = fname + else: + # It's a file path + fin = stack.enter_context(open(fname)) + + for linenum, line in enumerate(fin): + if isinstance(line, bytes): + line = line.decode("utf-8") + + line = line.strip() + # skip line comments + if line.startswith("#"): + continue + + elements = shlex.split(line) + if not len(elements) in [0, 2, 3]: + raise OSError( + f"Invalid entry in Pooch registry file '{fname}': " + f"expected 2 or 3 elements in line {linenum + 1} but got " + f"{len(elements)}. Offending entry: '{line}'" + ) + if elements: + file_name = elements[0] + file_checksum = elements[1] + if len(elements) == 3: + file_url = elements[2] + self.urls[file_name] = file_url + self.registry[file_name] = file_checksum.lower()
+ +
[docs] def load_registry_from_doi(self): + """ + Populate the registry using the data repository API + + Fill the registry with all the files available in the data repository, + along with their hashes. It will make a request to the data repository + API to retrieve this information. No file is downloaded during this + process. + + .. important:: + + This method is intended to be used only when the ``base_url`` is + a DOI. + """ + + # Ensure that this is indeed a DOI-based pooch + downloader = choose_downloader(self.base_url) + if not isinstance(downloader, DOIDownloader): + raise ValueError( + f"Invalid base_url '{self.base_url}': " + + "Pooch.load_registry_from_doi is only implemented for DOIs" + ) + + # Create a repository instance + doi = self.base_url.replace("doi:", "") + repository = doi_to_repository(doi) + + # Call registry population for this repository + return repository.populate_registry(self)
+ +
[docs] def is_available(self, fname, downloader=None): + """ + Check availability of a remote file without downloading it. + + Use this method when working with large files to check if they are + available for download. + + Parameters + ---------- + fname : str + The file name (relative to the *base_url* of the remote data + storage). + downloader : None or callable + If not None, then a function (or callable object) that will be + called to check the availability of the file on the server. See + :ref:`downloaders` for details. + + Returns + ------- + status : bool + True if the file is available for download. False otherwise. + + """ + self._assert_file_in_registry(fname) + url = self.get_url(fname) + if downloader is None: + downloader = choose_downloader(url) + try: + available = downloader(url, None, self, check_only=True) + except TypeError as error: + error_msg = ( + f"Downloader '{str(downloader)}' does not support availability checks." + ) + raise NotImplementedError(error_msg) from error + return available
+ + +def download_action(path, known_hash): + """ + Determine the action that is needed to get the file on disk. + + Parameters + ---------- + path : PathLike + The path to the file on disk. + known_hash : str + A known hash (checksum) of the file. Will be used to verify the + download or check if an existing file needs to be updated. By default, + will assume it's a SHA256 hash. To specify a different hashing method, + prepend the hash with ``algorithm:``, for example + ``md5:pw9co2iun29juoh`` or ``sha1:092odwhi2ujdp2du2od2odh2wod2``. + + Returns + ------- + action, verb : str + The action that must be taken and the English verb (infinitive form of + *action*) used in the log: + * ``'download'``: File does not exist locally and must be downloaded. + * ``'update'``: File exists locally but needs to be updated. + * ``'fetch'``: File exists locally and only need to inform its path. + + + """ + if not path.exists(): + action = "download" + verb = "Downloading" + elif not hash_matches(str(path), known_hash): + action = "update" + verb = "Updating" + else: + action = "fetch" + verb = "Fetching" + return action, verb + + +def stream_download(url, fname, known_hash, downloader, pooch=None, retry_if_failed=0): + """ + Stream the file and check that its hash matches the known one. + + The file is first downloaded to a temporary file name in the cache folder. + It will be moved to the desired file name only if the hash matches the + known hash. Otherwise, the temporary file is deleted. + + If the download fails for either a bad connection or a hash mismatch, we + will retry the download the specified number of times in case the failure + was due to a network error. + """ + # Lazy import requests to speed up import time + import requests.exceptions # pylint: disable=C0415 + + # Ensure the parent directory exists in case the file is in a subdirectory. + # Otherwise, move will cause an error. + if not fname.parent.exists(): + os.makedirs(str(fname.parent)) + download_attempts = 1 + retry_if_failed + max_wait = 10 + for i in range(download_attempts): + try: + # Stream the file to a temporary so that we can safely check its + # hash before overwriting the original. + with temporary_file(path=str(fname.parent)) as tmp: + downloader(url, tmp, pooch) + hash_matches(tmp, known_hash, strict=True, source=str(fname.name)) + shutil.move(tmp, str(fname)) + break + except (ValueError, requests.exceptions.RequestException): + if i == download_attempts - 1: + raise + retries_left = download_attempts - (i + 1) + get_logger().info( + "Failed to download '%s'. " + "Will attempt the download again %d more time%s.", + str(fname.name), + retries_left, + "s" if retries_left > 1 else "", + ) + time.sleep(min(i + 1, max_wait)) +
+ +
+ + + +
+
+ +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/_modules/pooch/downloaders.html b/v1.8.0/_modules/pooch/downloaders.html new file mode 100644 index 00000000..ecdb4df0 --- /dev/null +++ b/v1.8.0/_modules/pooch/downloaders.html @@ -0,0 +1,1598 @@ + + + + + + + + + + pooch.downloaders | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

+ +
+
+ +
+
+
+ +
+ +

Source code for pooch.downloaders

+# Copyright (c) 2018 The Pooch Developers.
+# Distributed under the terms of the BSD 3-Clause License.
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# This code is part of the Fatiando a Terra project (https://www.fatiando.org)
+#
+"""
+The classes that actually handle the downloads.
+"""
+import os
+import sys
+import ftplib
+
+import warnings
+
+from .utils import parse_url
+
+try:
+    from tqdm import tqdm
+except ImportError:
+    tqdm = None
+
+try:
+    import paramiko
+except ImportError:
+    paramiko = None
+
+
+def choose_downloader(url, progressbar=False):
+    """
+    Choose the appropriate downloader for the given URL based on the protocol.
+
+    Parameters
+    ----------
+    url : str
+        A URL (including protocol).
+    progressbar : bool or an arbitrary progress bar object
+        If True, will print a progress bar of the download to standard error
+        (stderr). Requires `tqdm <https://github.com/tqdm/tqdm>`__ to be
+        installed. Alternatively, an arbitrary progress bar object can be
+        passed. See :ref:`custom-progressbar` for details.
+
+    Returns
+    -------
+    downloader
+        A downloader class, like :class:`pooch.HTTPDownloader`,
+        :class:`pooch.FTPDownloader`, or :class: `pooch.SFTPDownloader`.
+
+    Examples
+    --------
+
+    >>> downloader = choose_downloader("http://something.com")
+    >>> print(downloader.__class__.__name__)
+    HTTPDownloader
+    >>> downloader = choose_downloader("https://something.com")
+    >>> print(downloader.__class__.__name__)
+    HTTPDownloader
+    >>> downloader = choose_downloader("ftp://something.com")
+    >>> print(downloader.__class__.__name__)
+    FTPDownloader
+    >>> downloader = choose_downloader("doi:DOI/filename.csv")
+    >>> print(downloader.__class__.__name__)
+    DOIDownloader
+
+    """
+    known_downloaders = {
+        "ftp": FTPDownloader,
+        "https": HTTPDownloader,
+        "http": HTTPDownloader,
+        "sftp": SFTPDownloader,
+        "doi": DOIDownloader,
+    }
+
+    parsed_url = parse_url(url)
+    if parsed_url["protocol"] not in known_downloaders:
+        raise ValueError(
+            f"Unrecognized URL protocol '{parsed_url['protocol']}' in '{url}'. "
+            f"Must be one of {known_downloaders.keys()}."
+        )
+    downloader = known_downloaders[parsed_url["protocol"]](progressbar=progressbar)
+    return downloader
+
+
+
[docs]class HTTPDownloader: # pylint: disable=too-few-public-methods + """ + Download manager for fetching files over HTTP/HTTPS. + + When called, downloads the given file URL into the specified local file. + Uses the :mod:`requests` library to manage downloads. + + Use with :meth:`pooch.Pooch.fetch` or :func:`pooch.retrieve` to customize + the download of files (for example, to use authentication or print a + progress bar). + + Parameters + ---------- + progressbar : bool or an arbitrary progress bar object + If True, will print a progress bar of the download to standard error + (stderr). Requires `tqdm <https://github.com/tqdm/tqdm>`__ to be + installed. Alternatively, an arbitrary progress bar object can be + passed. See :ref:`custom-progressbar` for details. + chunk_size : int + Files are streamed *chunk_size* bytes at a time instead of loading + everything into memory at one. Usually doesn't need to be changed. + **kwargs + All keyword arguments given when creating an instance of this class + will be passed to :func:`requests.get`. + + Examples + -------- + + Download one of the data files from the Pooch repository: + + >>> import os + >>> from pooch import __version__, check_version + >>> url = "https://github.com/fatiando/pooch/raw/{}/data/tiny-data.txt" + >>> url = url.format(check_version(__version__, fallback="main")) + >>> downloader = HTTPDownloader() + >>> # Not using with Pooch.fetch so no need to pass an instance of Pooch + >>> downloader(url=url, output_file="tiny-data.txt", pooch=None) + >>> os.path.exists("tiny-data.txt") + True + >>> with open("tiny-data.txt") as f: + ... print(f.read().strip()) + # A tiny data file for test purposes only + 1 2 3 4 5 6 + >>> os.remove("tiny-data.txt") + + Authentication can be handled by passing a user name and password to + :func:`requests.get`. All arguments provided when creating an instance of + the class are forwarded to :func:`requests.get`. We'll use + ``auth=(username, password)`` to use basic HTTPS authentication. The + https://httpbin.org website allows us to make a fake a login request using + whatever username and password we provide to it: + + >>> user = "doggo" + >>> password = "goodboy" + >>> # httpbin will ask for the user and password we provide in the URL + >>> url = f"https://httpbin.org/basic-auth/{user}/{password}" + >>> # Trying without the login credentials causes an error + >>> downloader = HTTPDownloader() + >>> try: + ... downloader(url=url, output_file="tiny-data.txt", pooch=None) + ... except Exception: + ... print("There was an error!") + There was an error! + >>> # Pass in the credentials to HTTPDownloader + >>> downloader = HTTPDownloader(auth=(user, password)) + >>> downloader(url=url, output_file="tiny-data.txt", pooch=None) + >>> with open("tiny-data.txt") as f: + ... for line in f: + ... print(line.rstrip()) + { + "authenticated": true, + "user": "doggo" + } + >>> os.remove("tiny-data.txt") + + """ + + def __init__(self, progressbar=False, chunk_size=1024, **kwargs): + self.kwargs = kwargs + self.progressbar = progressbar + self.chunk_size = chunk_size + if self.progressbar is True and tqdm is None: + raise ValueError("Missing package 'tqdm' required for progress bars.") + +
[docs] def __call__(self, url, output_file, pooch, check_only=False): + """ + Download the given URL over HTTP to the given output file. + + Uses :func:`requests.get`. + + Parameters + ---------- + url : str + The URL to the file you want to download. + output_file : str or file-like object + Path (and file name) to which the file will be downloaded. + pooch : :class:`~pooch.Pooch` + The instance of :class:`~pooch.Pooch` that is calling this method. + check_only : bool + If True, will only check if a file exists on the server and + **without downloading the file**. Will return ``True`` if the file + exists and ``False`` otherwise. + + Returns + ------- + availability : bool or None + If ``check_only==True``, returns a boolean indicating if the file + is available on the server. Otherwise, returns ``None``. + + """ + # Lazy import requests to speed up import time + import requests # pylint: disable=C0415 + + if check_only: + response = requests.head(url, allow_redirects=True) + available = bool(response.status_code == 200) + return available + + kwargs = self.kwargs.copy() + kwargs.setdefault("stream", True) + ispath = not hasattr(output_file, "write") + if ispath: + output_file = open(output_file, "w+b") + try: + response = requests.get(url, **kwargs) + response.raise_for_status() + content = response.iter_content(chunk_size=self.chunk_size) + total = int(response.headers.get("content-length", 0)) + if self.progressbar is True: + # Need to use ascii characters on Windows because there isn't + # always full unicode support + # (see https://github.com/tqdm/tqdm/issues/454) + use_ascii = bool(sys.platform == "win32") + progress = tqdm( + total=total, + ncols=79, + ascii=use_ascii, + unit="B", + unit_scale=True, + leave=True, + ) + elif self.progressbar: + progress = self.progressbar + progress.total = total + for chunk in content: + if chunk: + output_file.write(chunk) + output_file.flush() + if self.progressbar: + # Use the chunk size here because chunk may be much + # larger if the data are decompressed by requests after + # reading (happens with text files). + progress.update(self.chunk_size) + # Make sure the progress bar gets filled even if the actual number + # is chunks is smaller than expected. This happens when streaming + # text files that are compressed by the server when sending (gzip). + # Binary files don't experience this. + if self.progressbar: + progress.reset() + progress.update(total) + progress.close() + finally: + if ispath: + output_file.close() + return None
+ + +
[docs]class FTPDownloader: # pylint: disable=too-few-public-methods + """ + Download manager for fetching files over FTP. + + When called, downloads the given file URL into the specified local file. + Uses the :mod:`ftplib` module to manage downloads. + + Use with :meth:`pooch.Pooch.fetch` or :func:`pooch.retrieve` to customize + the download of files (for example, to use authentication or print a + progress bar). + + Parameters + ---------- + port : int + Port used for the FTP connection. + username : str + User name used to login to the server. Only needed if the server + requires authentication (i.e., no anonymous FTP). + password : str + Password used to login to the server. Only needed if the server + requires authentication (i.e., no anonymous FTP). Use the empty string + to indicate no password is required. + account : str + Some servers also require an "account" name for authentication. + timeout : int + Timeout in seconds for ftp socket operations, use None to mean no + timeout. + progressbar : bool + If True, will print a progress bar of the download to standard error + (stderr). Requires `tqdm <https://github.com/tqdm/tqdm>`__ to be + installed. **Custom progress bars are not yet supported.** + chunk_size : int + Files are streamed *chunk_size* bytes at a time instead of loading + everything into memory at one. Usually doesn't need to be changed. + + """ + + def __init__( + self, + port=21, + username="anonymous", + password="", + account="", + timeout=None, + progressbar=False, + chunk_size=1024, + ): + self.port = port + self.username = username + self.password = password + self.account = account + self.timeout = timeout + self.progressbar = progressbar + self.chunk_size = chunk_size + if self.progressbar is True and tqdm is None: + raise ValueError("Missing package 'tqdm' required for progress bars.") + +
[docs] def __call__(self, url, output_file, pooch, check_only=False): + """ + Download the given URL over FTP to the given output file. + + Parameters + ---------- + url : str + The URL to the file you want to download. + output_file : str or file-like object + Path (and file name) to which the file will be downloaded. + pooch : :class:`~pooch.Pooch` + The instance of :class:`~pooch.Pooch` that is calling this method. + check_only : bool + If True, will only check if a file exists on the server and + **without downloading the file**. Will return ``True`` if the file + exists and ``False`` otherwise. + + Returns + ------- + availability : bool or None + If ``check_only==True``, returns a boolean indicating if the file + is available on the server. Otherwise, returns ``None``. + + """ + parsed_url = parse_url(url) + ftp = ftplib.FTP(timeout=self.timeout) + ftp.connect(host=parsed_url["netloc"], port=self.port) + + if check_only: + directory, file_name = os.path.split(parsed_url["path"]) + try: + ftp.login(user=self.username, passwd=self.password, acct=self.account) + available = file_name in ftp.nlst(directory) + finally: + ftp.close() + return available + + ispath = not hasattr(output_file, "write") + if ispath: + output_file = open(output_file, "w+b") + try: + ftp.login(user=self.username, passwd=self.password, acct=self.account) + command = f"RETR {parsed_url['path']}" + if self.progressbar: + # Make sure the file is set to binary mode, otherwise we can't + # get the file size. See: https://stackoverflow.com/a/22093848 + ftp.voidcmd("TYPE I") + use_ascii = bool(sys.platform == "win32") + progress = tqdm( + total=int(ftp.size(parsed_url["path"])), + ncols=79, + ascii=use_ascii, + unit="B", + unit_scale=True, + leave=True, + ) + with progress: + + def callback(data): + "Update the progress bar and write to output" + progress.update(len(data)) + output_file.write(data) + + ftp.retrbinary(command, callback, blocksize=self.chunk_size) + else: + ftp.retrbinary(command, output_file.write, blocksize=self.chunk_size) + finally: + ftp.quit() + if ispath: + output_file.close() + return None
+ + +
[docs]class SFTPDownloader: # pylint: disable=too-few-public-methods + """ + Download manager for fetching files over SFTP. + + When called, downloads the given file URL into the specified local file. + Requires `paramiko <https://github.com/paramiko/paramiko>`__ to be + installed. + + Use with :meth:`pooch.Pooch.fetch` or :func:`pooch.retrieve` to customize + the download of files (for example, to use authentication or print a + progress bar). + + Parameters + ---------- + port : int + Port used for the SFTP connection. + username : str + User name used to login to the server. Only needed if the server + requires authentication (i.e., no anonymous SFTP). + password : str + Password used to login to the server. Only needed if the server + requires authentication (i.e., no anonymous SFTP). Use the empty + string to indicate no password is required. + timeout : int + Timeout in seconds for sftp socket operations, use None to mean no + timeout. + progressbar : bool or an arbitrary progress bar object + If True, will print a progress bar of the download to standard + error (stderr). Requires `tqdm <https://github.com/tqdm/tqdm>`__ to + be installed. + + """ + + def __init__( + self, + port=22, + username="anonymous", + password="", + account="", + timeout=None, + progressbar=False, + ): + self.port = port + self.username = username + self.password = password + self.account = account + self.timeout = timeout + self.progressbar = progressbar + # Collect errors and raise only once so that both missing packages are + # captured. Otherwise, the user is only warned of one of them at a + # time (and we can't test properly when they are both missing). + errors = [] + if self.progressbar and tqdm is None: + errors.append("Missing package 'tqdm' required for progress bars.") + if paramiko is None: + errors.append("Missing package 'paramiko' required for SFTP downloads.") + if errors: + raise ValueError(" ".join(errors)) + +
[docs] def __call__(self, url, output_file, pooch): + """ + Download the given URL over SFTP to the given output file. + + The output file must be given as a string (file name/path) and not an + open file object! Otherwise, paramiko cannot save to that file. + + Parameters + ---------- + url : str + The URL to the file you want to download. + output_file : str + Path (and file name) to which the file will be downloaded. **Cannot + be a file object**. + pooch : :class:`~pooch.Pooch` + The instance of :class:`~pooch.Pooch` that is calling this method. + """ + parsed_url = parse_url(url) + connection = paramiko.Transport(sock=(parsed_url["netloc"], self.port)) + sftp = None + try: + connection.connect(username=self.username, password=self.password) + sftp = paramiko.SFTPClient.from_transport(connection) + sftp.get_channel().settimeout = self.timeout + if self.progressbar: + size = int(sftp.stat(parsed_url["path"]).st_size) + use_ascii = bool(sys.platform == "win32") + progress = tqdm( + total=size, + ncols=79, + ascii=use_ascii, + unit="B", + unit_scale=True, + leave=True, + ) + if self.progressbar: + with progress: + + def callback(current, total): + "Update the progress bar and write to output" + progress.total = int(total) + progress.update(int(current - progress.n)) + + sftp.get(parsed_url["path"], output_file, callback=callback) + else: + sftp.get(parsed_url["path"], output_file) + finally: + connection.close() + if sftp is not None: + sftp.close()
+ + +
[docs]class DOIDownloader: # pylint: disable=too-few-public-methods + """ + Download manager for fetching files from Digital Object Identifiers (DOIs). + + Open-access data repositories often issue Digital Object Identifiers (DOIs) + for data which provide a stable link and citation point. The trick is + finding out the download URL for a file given the DOI. + + When called, this downloader uses the repository's public API to find out + the download URL from the DOI and file name. It then uses + :class:`pooch.HTTPDownloader` to download the URL into the specified local + file. Allowing "URL"s to be specified with the DOI instead of the actual + HTTP download link. Uses the :mod:`requests` library to manage downloads + and interact with the APIs. + + The **format of the "URL"** is: ``doi:{DOI}/{file name}``. + + Notice that there are no ``//`` like in HTTP/FTP and you must specify a + file name after the DOI (separated by a ``/``). + + Use with :meth:`pooch.Pooch.fetch` or :func:`pooch.retrieve` to be able to + download files given the DOI instead of an HTTP link. + + Supported repositories: + + * `figshare <https://www.figshare.com>`__ + * `Zenodo <https://www.zenodo.org>`__ + * `Dataverse <https://dataverse.org/>`__ instances + + .. attention:: + + DOIs from other repositories **will not work** since we need to access + their particular APIs to find the download links. We welcome + suggestions and contributions adding new repositories. + + Parameters + ---------- + progressbar : bool or an arbitrary progress bar object + If True, will print a progress bar of the download to standard error + (stderr). Requires `tqdm <https://github.com/tqdm/tqdm>`__ to be + installed. Alternatively, an arbitrary progress bar object can be + passed. See :ref:`custom-progressbar` for details. + chunk_size : int + Files are streamed *chunk_size* bytes at a time instead of loading + everything into memory at one. Usually doesn't need to be changed. + **kwargs + All keyword arguments given when creating an instance of this class + will be passed to :func:`requests.get`. + + Examples + -------- + + Download one of the data files from the figshare archive of Pooch test + data: + + >>> import os + >>> downloader = DOIDownloader() + >>> url = "doi:10.6084/m9.figshare.14763051.v1/tiny-data.txt" + >>> # Not using with Pooch.fetch so no need to pass an instance of Pooch + >>> downloader(url=url, output_file="tiny-data.txt", pooch=None) + >>> os.path.exists("tiny-data.txt") + True + >>> with open("tiny-data.txt") as f: + ... print(f.read().strip()) + # A tiny data file for test purposes only + 1 2 3 4 5 6 + >>> os.remove("tiny-data.txt") + + Same thing but for our Zenodo archive: + + >>> url = "doi:10.5281/zenodo.4924875/tiny-data.txt" + >>> downloader(url=url, output_file="tiny-data.txt", pooch=None) + >>> os.path.exists("tiny-data.txt") + True + >>> with open("tiny-data.txt") as f: + ... print(f.read().strip()) + # A tiny data file for test purposes only + 1 2 3 4 5 6 + >>> os.remove("tiny-data.txt") + + """ + + def __init__(self, progressbar=False, chunk_size=1024, **kwargs): + self.kwargs = kwargs + self.progressbar = progressbar + self.chunk_size = chunk_size + +
[docs] def __call__(self, url, output_file, pooch): + """ + Download the given DOI URL over HTTP to the given output file. + + Uses the repository's API to determine the actual HTTP download URL + from the given DOI. + + Uses :func:`requests.get`. + + Parameters + ---------- + url : str + The URL to the file you want to download. + output_file : str or file-like object + Path (and file name) to which the file will be downloaded. + pooch : :class:`~pooch.Pooch` + The instance of :class:`~pooch.Pooch` that is calling this method. + + """ + + parsed_url = parse_url(url) + data_repository = doi_to_repository(parsed_url["netloc"]) + + # Resolve the URL + file_name = parsed_url["path"] + # remove the leading slash in the path + if file_name[0] == "/": + file_name = file_name[1:] + download_url = data_repository.download_url(file_name) + + # Instantiate the downloader object + downloader = HTTPDownloader( + progressbar=self.progressbar, chunk_size=self.chunk_size, **self.kwargs + ) + downloader(download_url, output_file, pooch)
+ + +def doi_to_url(doi): + """ + Follow a DOI link to resolve the URL of the archive. + + Parameters + ---------- + doi : str + The DOI of the archive. + + Returns + ------- + url : str + The URL of the archive in the data repository. + + """ + # Lazy import requests to speed up import time + import requests # pylint: disable=C0415 + + # Use doi.org to resolve the DOI to the repository website. + response = requests.get(f"https://doi.org/{doi}") + url = response.url + if 400 <= response.status_code < 600: + raise ValueError( + f"Archive with doi:{doi} not found (see {url}). Is the DOI correct?" + ) + return url + + +def doi_to_repository(doi): + """ + Instantiate a data repository instance from a given DOI. + + This function implements the chain of responsibility dispatch + to the correct data repository class. + + Parameters + ---------- + doi : str + The DOI of the archive. + + Returns + ------- + data_repository : DataRepository + The data repository object + """ + + # This should go away in a separate issue: DOI handling should + # not rely on the (non-)existence of trailing slashes. The issue + # is documented in https://github.com/fatiando/pooch/issues/324 + if doi[-1] == "/": + doi = doi[:-1] + + repositories = [ + FigshareRepository, + ZenodoRepository, + DataverseRepository, + ] + + # Extract the DOI and the repository information + archive_url = doi_to_url(doi) + + # Try the converters one by one until one of them returned a URL + data_repository = None + for repo in repositories: + if data_repository is None: + data_repository = repo.initialize( + archive_url=archive_url, + doi=doi, + ) + + if data_repository is None: + repository = parse_url(archive_url)["netloc"] + raise ValueError( + f"Invalid data repository '{repository}'. " + "To request or contribute support for this repository, " + "please open an issue at https://github.com/fatiando/pooch/issues" + ) + + return data_repository + + +class DataRepository: # pylint: disable=too-few-public-methods, missing-class-docstring + @classmethod + def initialize(cls, doi, archive_url): # pylint: disable=unused-argument + """ + Initialize the data repository if the given URL points to a + corresponding repository. + + Initializes a data repository object. This is done as part of + a chain of responsibility. If the class cannot handle the given + repository URL, it returns `None`. Otherwise a `DataRepository` + instance is returned. + + Parameters + ---------- + doi : str + The DOI that identifies the repository + archive_url : str + The resolved URL for the DOI + """ + + return None # pragma: no cover + + def download_url(self, file_name): + """ + Use the repository API to get the download URL for a file given + the archive URL. + + Parameters + ---------- + file_name : str + The name of the file in the archive that will be downloaded. + + Returns + ------- + download_url : str + The HTTP URL that can be used to download the file. + """ + + raise NotImplementedError # pragma: no cover + + def populate_registry(self, pooch): + """ + Populate the registry using the data repository's API + + Parameters + ---------- + pooch : Pooch + The pooch instance that the registry will be added to. + """ + + raise NotImplementedError # pragma: no cover + + +class ZenodoRepository(DataRepository): # pylint: disable=missing-class-docstring + base_api_url = "https://zenodo.org/api/records" + + def __init__(self, doi, archive_url): + self.archive_url = archive_url + self.doi = doi + self._api_response = None + self._api_version = None + + @classmethod + def initialize(cls, doi, archive_url): + """ + Initialize the data repository if the given URL points to a + corresponding repository. + + Initializes a data repository object. This is done as part of + a chain of responsibility. If the class cannot handle the given + repository URL, it returns `None`. Otherwise a `DataRepository` + instance is returned. + + Parameters + ---------- + doi : str + The DOI that identifies the repository + archive_url : str + The resolved URL for the DOI + """ + + # Check whether this is a Zenodo URL + parsed_archive_url = parse_url(archive_url) + if parsed_archive_url["netloc"] != "zenodo.org": + return None + + return cls(doi, archive_url) + + @property + def api_response(self): + """Cached API response from Zenodo""" + if self._api_response is None: + # Lazy import requests to speed up import time + import requests # pylint: disable=C0415 + + article_id = self.archive_url.split("/")[-1] + self._api_response = requests.get( + f"{self.base_api_url}/{article_id}" + ).json() + + return self._api_response + + @property + def api_version(self): + """ + Version of the Zenodo API we are interacting with + + The versions can either be : + + - ``"legacy"``: corresponds to the Zenodo API that was supported until + 2023-10-12 (before the migration to InvenioRDM). + - ``"new"``: corresponds to the new API that went online on 2023-10-13 + after the migration to InvenioRDM. + + The ``"new"`` API breaks backward compatibility with the ``"legacy"`` + one and could probably be replaced by an updated version that restores + the behaviour of the ``"legacy"`` one. + + Returns + ------- + str + """ + if self._api_version is None: + if all(["key" in file for file in self.api_response["files"]]): + self._api_version = "legacy" + elif all(["filename" in file for file in self.api_response["files"]]): + self._api_version = "new" + else: + raise ValueError( + "Couldn't determine the version of the Zenodo API for " + f"{self.archive_url} (doi:{self.doi})." + ) + return self._api_version + + def download_url(self, file_name): + """ + Use the repository API to get the download URL for a file given + the archive URL. + + Parameters + ---------- + file_name : str + The name of the file in the archive that will be downloaded. + + Returns + ------- + download_url : str + The HTTP URL that can be used to download the file. + + Notes + ----- + After Zenodo migrated to InvenioRDM on Oct 2023, their API changed. The + link to the desired files that appears in the API response leads to 404 + errors (by 2023-10-17). The files are available in the following url: + ``https://zenodo.org/records/{article_id}/files/{file_name}?download=1``. + + This method supports both the legacy and the new API. + """ + # Create list of files in the repository + if self.api_version == "legacy": + files = {item["key"]: item for item in self.api_response["files"]} + else: + files = [item["filename"] for item in self.api_response["files"]] + # Check if file exists in the repository + if file_name not in files: + raise ValueError( + f"File '{file_name}' not found in data archive " + f"{self.archive_url} (doi:{self.doi})." + ) + # Build download url + if self.api_version == "legacy": + download_url = files[file_name]["links"]["self"] + else: + article_id = self.api_response["id"] + download_url = ( + f"https://zenodo.org/records/{article_id}/files/{file_name}?download=1" + ) + return download_url + + def populate_registry(self, pooch): + """ + Populate the registry using the data repository's API + + Parameters + ---------- + pooch : Pooch + The pooch instance that the registry will be added to. + + Notes + ----- + After Zenodo migrated to InvenioRDM on Oct 2023, their API changed. The + checksums for each file listed in the API reference is now an md5 sum. + + This method supports both the legacy and the new API. + """ + for filedata in self.api_response["files"]: + checksum = filedata["checksum"] + if self.api_version == "legacy": + key = "key" + else: + key = "filename" + checksum = f"md5:{checksum}" + pooch.registry[filedata[key]] = checksum + + +class FigshareRepository(DataRepository): # pylint: disable=missing-class-docstring + def __init__(self, doi, archive_url): + self.archive_url = archive_url + self.doi = doi + self._api_response = None + + @classmethod + def initialize(cls, doi, archive_url): + """ + Initialize the data repository if the given URL points to a + corresponding repository. + + Initializes a data repository object. This is done as part of + a chain of responsibility. If the class cannot handle the given + repository URL, it returns `None`. Otherwise a `DataRepository` + instance is returned. + + Parameters + ---------- + doi : str + The DOI that identifies the repository + archive_url : str + The resolved URL for the DOI + """ + + # Check whether this is a Figshare URL + parsed_archive_url = parse_url(archive_url) + if parsed_archive_url["netloc"] != "figshare.com": + return None + + return cls(doi, archive_url) + + def _parse_version_from_doi(self): + """ + Parse version from the doi + + Return None if version is not available in the doi. + """ + # Get suffix of the doi + _, suffix = self.doi.split("/") + # Split the suffix by dots and keep the last part + last_part = suffix.split(".")[-1] + # Parse the version from the last part + if last_part[0] != "v": + return None + version = int(last_part[1:]) + return version + + @property + def api_response(self): + """Cached API response from Figshare""" + if self._api_response is None: + # Lazy import requests to speed up import time + import requests # pylint: disable=C0415 + + # Use the figshare API to find the article ID from the DOI + article = requests.get( + f"https://api.figshare.com/v2/articles?doi={self.doi}" + ).json()[0] + article_id = article["id"] + # Parse desired version from the doi + version = self._parse_version_from_doi() + # With the ID and version, we can get a list of files and their + # download links + if version is None: + # Figshare returns the latest version available when no version + # is specified through the DOI. + warnings.warn( + f"The Figshare DOI '{self.doi}' doesn't specify which version of " + "the repository should be used. " + "Figshare will point to the latest version available.", + UserWarning, + ) + # Define API url using only the article id + # (figshare will resolve the latest version) + api_url = f"https://api.figshare.com/v2/articles/{article_id}" + else: + # Define API url using article id and the desired version + # Get list of files using article id and the version + api_url = ( + "https://api.figshare.com/v2/articles/" + f"{article_id}/versions/{version}" + ) + # Make the request and return the files in the figshare repository + response = requests.get(api_url) + response.raise_for_status() + self._api_response = response.json()["files"] + + return self._api_response + + def download_url(self, file_name): + """ + Use the repository API to get the download URL for a file given + the archive URL. + + Parameters + ---------- + file_name : str + The name of the file in the archive that will be downloaded. + + Returns + ------- + download_url : str + The HTTP URL that can be used to download the file. + """ + files = {item["name"]: item for item in self.api_response} + if file_name not in files: + raise ValueError( + f"File '{file_name}' not found in data archive {self.archive_url} (doi:{self.doi})." + ) + download_url = files[file_name]["download_url"] + return download_url + + def populate_registry(self, pooch): + """ + Populate the registry using the data repository's API + + Parameters + ---------- + pooch : Pooch + The pooch instance that the registry will be added to. + """ + + for filedata in self.api_response: + pooch.registry[filedata["name"]] = f"md5:{filedata['computed_md5']}" + + +class DataverseRepository(DataRepository): # pylint: disable=missing-class-docstring + def __init__(self, doi, archive_url): + self.archive_url = archive_url + self.doi = doi + self._api_response = None + + @classmethod + def initialize(cls, doi, archive_url): + """ + Initialize the data repository if the given URL points to a + corresponding repository. + + Initializes a data repository object. This is done as part of + a chain of responsibility. If the class cannot handle the given + repository URL, it returns `None`. Otherwise a `DataRepository` + instance is returned. + + Parameters + ---------- + doi : str + The DOI that identifies the repository + archive_url : str + The resolved URL for the DOI + """ + # Access the DOI as if this was a DataVerse instance + response = cls._get_api_response(doi, archive_url) + + # If we failed, this is probably not a DataVerse instance + if 400 <= response.status_code < 600: + return None + + # Initialize the repository and overwrite the api response + repository = cls(doi, archive_url) + repository.api_response = response + return repository + + @classmethod + def _get_api_response(cls, doi, archive_url): + """ + Perform the actual API request + + This has been separated into a separate ``classmethod``, as it can be + used prior and after the initialization. + """ + # Lazy import requests to speed up import time + import requests # pylint: disable=C0415 + + parsed = parse_url(archive_url) + response = requests.get( + f"{parsed['protocol']}://{parsed['netloc']}/api/datasets/" + f":persistentId?persistentId=doi:{doi}" + ) + return response + + @property + def api_response(self): + """Cached API response from a DataVerse instance""" + + if self._api_response is None: + self._api_response = self._get_api_response( + self.doi, self.archive_url + ) # pragma: no cover + + return self._api_response + + @api_response.setter + def api_response(self, response): + """Update the cached API response""" + + self._api_response = response + + def download_url(self, file_name): + """ + Use the repository API to get the download URL for a file given + the archive URL. + + Parameters + ---------- + file_name : str + The name of the file in the archive that will be downloaded. + + Returns + ------- + download_url : str + The HTTP URL that can be used to download the file. + """ + parsed = parse_url(self.archive_url) + + # Iterate over the given files until we find one of the requested name + for filedata in self.api_response.json()["data"]["latestVersion"]["files"]: + if file_name == filedata["dataFile"]["filename"]: + return ( + f"{parsed['protocol']}://{parsed['netloc']}/api/access/datafile/" + f":persistentId?persistentId={filedata['dataFile']['persistentId']}" + ) + + raise ValueError( + f"File '{file_name}' not found in data archive {self.archive_url} (doi:{self.doi})." + ) + + def populate_registry(self, pooch): + """ + Populate the registry using the data repository's API + + Parameters + ---------- + pooch : Pooch + The pooch instance that the registry will be added to. + """ + + for filedata in self.api_response.json()["data"]["latestVersion"]["files"]: + pooch.registry[ + filedata["dataFile"]["filename"] + ] = f"md5:{filedata['dataFile']['md5']}" +
+ +
+ + + +
+
+ +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/_modules/pooch/hashes.html b/v1.8.0/_modules/pooch/hashes.html new file mode 100644 index 00000000..92f2685a --- /dev/null +++ b/v1.8.0/_modules/pooch/hashes.html @@ -0,0 +1,678 @@ + + + + + + + + + + pooch.hashes | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

+ +
+
+ +
+
+
+ +
+ +

Source code for pooch.hashes

+# Copyright (c) 2018 The Pooch Developers.
+# Distributed under the terms of the BSD 3-Clause License.
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# This code is part of the Fatiando a Terra project (https://www.fatiando.org)
+#
+"""
+Calculating and checking file hashes.
+"""
+import hashlib
+import functools
+from pathlib import Path
+
+# From the docs: https://docs.python.org/3/library/hashlib.html#hashlib.new
+#   The named constructors are much faster than new() and should be
+#   preferred.
+# Need to fallback on new() for some algorithms.
+ALGORITHMS_AVAILABLE = {
+    alg: getattr(hashlib, alg, functools.partial(hashlib.new, alg))
+    for alg in hashlib.algorithms_available
+}
+
+try:
+    import xxhash
+
+    # xxhash doesn't have a list of available algorithms yet.
+    # https://github.com/ifduyue/python-xxhash/issues/48
+    ALGORITHMS_AVAILABLE.update(
+        {
+            alg: getattr(xxhash, alg, None)
+            for alg in ["xxh128", "xxh64", "xxh32", "xxh3_128", "xxh3_64"]
+        }
+    )
+    # The xxh3 algorithms are only available for version>=2.0. Set to None and
+    # remove to ensure backwards compatibility.
+    ALGORITHMS_AVAILABLE = {
+        alg: func for alg, func in ALGORITHMS_AVAILABLE.items() if func is not None
+    }
+except ImportError:
+    pass
+
+
+
[docs]def file_hash(fname, alg="sha256"): + """ + Calculate the hash of a given file. + + Useful for checking if a file has changed or been corrupted. + + Parameters + ---------- + fname : str + The name of the file. + alg : str + The type of the hashing algorithm + + Returns + ------- + hash : str + The hash of the file. + + Examples + -------- + + >>> fname = "test-file-for-hash.txt" + >>> with open(fname, "w") as f: + ... __ = f.write("content of the file") + >>> print(file_hash(fname)) + 0fc74468e6a9a829f103d069aeb2bb4f8646bad58bf146bb0e3379b759ec4a00 + >>> import os + >>> os.remove(fname) + + """ + if alg not in ALGORITHMS_AVAILABLE: + raise ValueError( + f"Algorithm '{alg}' not available to the pooch library. " + "Only the following algorithms are available " + f"{list(ALGORITHMS_AVAILABLE.keys())}." + ) + # Calculate the hash in chunks to avoid overloading the memory + chunksize = 65536 + hasher = ALGORITHMS_AVAILABLE[alg]() + with open(fname, "rb") as fin: + buff = fin.read(chunksize) + while buff: + hasher.update(buff) + buff = fin.read(chunksize) + return hasher.hexdigest()
+ + +def hash_algorithm(hash_string): + """ + Parse the name of the hash method from the hash string. + + The hash string should have the following form ``algorithm:hash``, where + algorithm can be the name of any algorithm known to :mod:`hashlib`. + + If the algorithm is omitted or the hash string is None, will default to + ``"sha256"``. + + Parameters + ---------- + hash_string : str + The hash string with optional algorithm prepended. + + Returns + ------- + hash_algorithm : str + The name of the algorithm. + + Examples + -------- + + >>> print(hash_algorithm("qouuwhwd2j192y1lb1iwgowdj2898wd2d9")) + sha256 + >>> print(hash_algorithm("md5:qouuwhwd2j192y1lb1iwgowdj2898wd2d9")) + md5 + >>> print(hash_algorithm("sha256:qouuwhwd2j192y1lb1iwgowdj2898wd2d9")) + sha256 + >>> print(hash_algorithm("SHA256:qouuwhwd2j192y1lb1iwgowdj2898wd2d9")) + sha256 + >>> print(hash_algorithm("xxh3_64:qouuwhwd2j192y1lb1iwgowdj2898wd2d9")) + xxh3_64 + >>> print(hash_algorithm(None)) + sha256 + + """ + default = "sha256" + if hash_string is None: + algorithm = default + elif ":" not in hash_string: + algorithm = default + else: + algorithm = hash_string.split(":")[0] + return algorithm.lower() + + +def hash_matches(fname, known_hash, strict=False, source=None): + """ + Check if the hash of a file matches a known hash. + + If the *known_hash* is None, will always return True. + + Coverts hashes to lowercase before comparison to avoid system specific + mismatches between hashes in the registry and computed hashes. + + Parameters + ---------- + fname : str or PathLike + The path to the file. + known_hash : str + The known hash. Optionally, prepend ``alg:`` to the hash to specify the + hashing algorithm. Default is SHA256. + strict : bool + If True, will raise a :class:`ValueError` if the hash does not match + informing the user that the file may be corrupted. + source : str + The source of the downloaded file (name or URL, for example). Will be + used in the error message if *strict* is True. Has no other use other + than reporting to the user where the file came from in case of hash + mismatch. If None, will default to *fname*. + + Returns + ------- + is_same : bool + True if the hash matches, False otherwise. + + """ + if known_hash is None: + return True + algorithm = hash_algorithm(known_hash) + new_hash = file_hash(fname, alg=algorithm) + matches = new_hash.lower() == known_hash.split(":")[-1].lower() + if strict and not matches: + if source is None: + source = str(fname) + raise ValueError( + f"{algorithm.upper()} hash of downloaded file ({source}) does not match" + f" the known hash: expected {known_hash} but got {new_hash}. Deleted" + " download for safety. The downloaded file may have been corrupted or" + " the known hash may be outdated." + ) + return matches + + +
[docs]def make_registry(directory, output, recursive=True): + """ + Make a registry of files and hashes for the given directory. + + This is helpful if you have many files in your test dataset as it keeps you + from needing to manually update the registry. + + Parameters + ---------- + directory : str + Directory of the test data to put in the registry. All file names in + the registry will be relative to this directory. + output : str + Name of the output registry file. + recursive : bool + If True, will recursively look for files in subdirectories of + *directory*. + + """ + directory = Path(directory) + if recursive: + pattern = "**/*" + else: + pattern = "*" + + files = sorted( + str(path.relative_to(directory)) + for path in directory.glob(pattern) + if path.is_file() + ) + + hashes = [file_hash(str(directory / fname)) for fname in files] + + with open(output, "w") as outfile: + for fname, fhash in zip(files, hashes): + # Only use Unix separators for the registry so that we don't go + # insane dealing with file paths. + outfile.write("{} {}\n".format(fname.replace("\\", "/"), fhash))
+
+ +
+ + + +
+
+ +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/_modules/pooch/processors.html b/v1.8.0/_modules/pooch/processors.html new file mode 100644 index 00000000..2e3660b6 --- /dev/null +++ b/v1.8.0/_modules/pooch/processors.html @@ -0,0 +1,827 @@ + + + + + + + + + + pooch.processors | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

+ +
+
+ +
+
+
+ +
+ +

Source code for pooch.processors

+# Copyright (c) 2018 The Pooch Developers.
+# Distributed under the terms of the BSD 3-Clause License.
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# This code is part of the Fatiando a Terra project (https://www.fatiando.org)
+#
+# pylint: disable=line-too-long
+"""
+Post-processing hooks
+"""
+import os
+import bz2
+import gzip
+import lzma
+import shutil
+from zipfile import ZipFile
+from tarfile import TarFile
+
+from .utils import get_logger
+
+
+class ExtractorProcessor:  # pylint: disable=too-few-public-methods
+    """
+    Base class for extractions from compressed archives.
+
+    Subclasses can be used with :meth:`pooch.Pooch.fetch` and
+    :func:`pooch.retrieve` to unzip a downloaded data file into a folder in the
+    local data store. :meth:`~pooch.Pooch.fetch` will return a list with the
+    names of the extracted files instead of the archive.
+
+    Parameters
+    ----------
+    members : list or None
+        If None, will unpack all files in the archive. Otherwise, *members*
+        must be a list of file names to unpack from the archive. Only these
+        files will be unpacked.
+
+    """
+
+    # String appended to unpacked archive. To be implemented in subclass
+    suffix = None
+
+    def __init__(self, members=None, extract_dir=None):
+        self.members = members
+        self.extract_dir = extract_dir
+
+    def __call__(self, fname, action, pooch):
+        """
+        Extract all files from the given archive.
+
+        Parameters
+        ----------
+        fname : str
+            Full path of the zipped file in local storage.
+        action : str
+            Indicates what action was taken by :meth:`pooch.Pooch.fetch` or
+            :func:`pooch.retrieve`:
+
+            * ``"download"``: File didn't exist locally and was downloaded
+            * ``"update"``: Local file was outdated and was re-download
+            * ``"fetch"``: File exists and is updated so it wasn't downloaded
+
+        pooch : :class:`pooch.Pooch`
+            The instance of :class:`pooch.Pooch` that is calling this.
+
+        Returns
+        -------
+        fnames : list of str
+            A list of the full path to all files in the extracted archive.
+
+        """
+        if self.suffix is None and self.extract_dir is None:
+            raise NotImplementedError(
+                "Derived classes must define either a 'suffix' attribute or "
+                "an 'extract_dir' attribute."
+            )
+        if self.extract_dir is None:
+            self.extract_dir = fname + self.suffix
+        else:
+            archive_dir = fname.rsplit(os.path.sep, maxsplit=1)[0]
+            self.extract_dir = os.path.join(archive_dir, self.extract_dir)
+        if (
+            (action in ("update", "download"))
+            or (not os.path.exists(self.extract_dir))
+            or (
+                (self.members is not None)
+                and (
+                    not all(
+                        os.path.exists(os.path.join(self.extract_dir, m))
+                        for m in self.members
+                    )
+                )
+            )
+        ):
+            # Make sure that the folder with the extracted files exists
+            os.makedirs(self.extract_dir, exist_ok=True)
+            self._extract_file(fname, self.extract_dir)
+
+        # Get a list of all file names (including subdirectories) in our folder
+        # of unzipped files, filtered by the given members list
+        fnames = []
+        for path, _, files in os.walk(self.extract_dir):
+            for filename in files:
+                relpath = os.path.normpath(
+                    os.path.join(os.path.relpath(path, self.extract_dir), filename)
+                )
+                if self.members is None or any(
+                    relpath.startswith(os.path.normpath(m)) for m in self.members
+                ):
+                    fnames.append(os.path.join(path, filename))
+
+        return fnames
+
+    def _extract_file(self, fname, extract_dir):
+        """
+        This method receives an argument for the archive to extract and the
+        destination path. MUST BE IMPLEMENTED BY CHILD CLASSES.
+        """
+        raise NotImplementedError
+
+
+
[docs]class Unzip(ExtractorProcessor): # pylint: disable=too-few-public-methods + """ + Processor that unpacks a zip archive and returns a list of all files. + + Use with :meth:`pooch.Pooch.fetch` or :func:`pooch.retrieve` to unzip a + downloaded data file into a folder in the local data store. The + method/function will return a list with the names of the unzipped files + instead of the zip archive. + + The output folder is ``{fname}.unzip``. + + Parameters + ---------- + members : list or None + If None, will unpack all files in the zip archive. Otherwise, *members* + must be a list of file names to unpack from the archive. Only these + files will be unpacked. + extract_dir : str or None + If None, files will be unpacked to the default location (a folder in + the same location as the downloaded zip file, with the suffix + ``.unzip`` added). Otherwise, files will be unpacked to + ``extract_dir``, which is interpreted as a *relative path* (relative to + the cache location provided by :func:`pooch.retrieve` or + :meth:`pooch.Pooch.fetch`). + + """ + + suffix = ".unzip" + + def _extract_file(self, fname, extract_dir): + """ + This method receives an argument for the archive to extract and the + destination path. + """ + with ZipFile(fname, "r") as zip_file: + if self.members is None: + get_logger().info( + "Unzipping contents of '%s' to '%s'", fname, extract_dir + ) + # Unpack all files from the archive into our new folder + zip_file.extractall(path=extract_dir) + else: + for member in self.members: + get_logger().info( + "Extracting '%s' from '%s' to '%s'", member, fname, extract_dir + ) + # If the member is a dir, we need to get the names of the + # elements it contains for extraction (ZipFile does not + # support dirs on .extract). If it's not a dir, this will + # only include the member itself. + # Based on: + # https://stackoverflow.com/questions/8008829/extract-only-a-single-directory-from-tar + subdir_members = [ + name + for name in zip_file.namelist() + if os.path.normpath(name).startswith(os.path.normpath(member)) + ] + # Extract the data file from within the archive + zip_file.extractall(members=subdir_members, path=extract_dir)
+ + +
[docs]class Untar(ExtractorProcessor): # pylint: disable=too-few-public-methods + """ + Processor that unpacks a tar archive and returns a list of all files. + + Use with :meth:`pooch.Pooch.fetch` or :func:`pooch.retrieve` to untar a + downloaded data file into a folder in the local data store. The + method/function will return a list with the names of the extracted files + instead of the archive. + + The output folder is ``{fname}.untar``. + + + Parameters + ---------- + members : list or None + If None, will unpack all files in the archive. Otherwise, *members* + must be a list of file names to unpack from the archive. Only these + files will be unpacked. + extract_dir : str or None + If None, files will be unpacked to the default location (a folder in + the same location as the downloaded tar file, with the suffix + ``.untar`` added). Otherwise, files will be unpacked to + ``extract_dir``, which is interpreted as a *relative path* (relative to + the cache location provided by :func:`pooch.retrieve` or + :meth:`pooch.Pooch.fetch`). + """ + + suffix = ".untar" + + def _extract_file(self, fname, extract_dir): + """ + This method receives an argument for the archive to extract and the + destination path. + """ + with TarFile.open(fname, "r") as tar_file: + if self.members is None: + get_logger().info( + "Untarring contents of '%s' to '%s'", fname, extract_dir + ) + # Unpack all files from the archive into our new folder + tar_file.extractall(path=extract_dir) + else: + for member in self.members: + get_logger().info( + "Extracting '%s' from '%s' to '%s'", member, fname, extract_dir + ) + # If the member is a dir, we need to get the names of the + # elements it contains for extraction (TarFile does not + # support dirs on .extract). If it's not a dir, this will + # only include the member itself. + # Based on: + # https://stackoverflow.com/questions/8008829/extract-only-a-single-directory-from-tar + # Can't use .getnames because extractall expects TarInfo + # objects. + subdir_members = [ + info + for info in tar_file.getmembers() + if os.path.normpath(info.name).startswith( + os.path.normpath(member) + ) + ] + # Extract the data file from within the archive + tar_file.extractall(members=subdir_members, path=extract_dir)
+ + +
[docs]class Decompress: # pylint: disable=too-few-public-methods + """ + Processor that decompress a file and returns the decompressed version. + + Use with :meth:`pooch.Pooch.fetch` or :func:`pooch.retrieve` to decompress + a downloaded data file so that it can be easily opened. Useful for data + files that take a long time to decompress (exchanging disk space for + speed). + + Supported decompression methods are LZMA (``.xz``), bzip2 (``.bz2``), and + gzip (``.gz``). + + File names with the standard extensions (see above) can use + ``method="auto"`` to automatically determine the compression method. This + can be overwritten by setting the *method* argument. + + .. note:: + + To unpack zip and tar archives with one or more files, use + :class:`pooch.Unzip` and :class:`pooch.Untar` instead. + + The output file is ``{fname}.decomp`` by default but it can be changed by + setting the ``name`` parameter. + + .. warning:: + + Passing in ``name`` can cause existing data to be lost! For example, if + a file already exists with the specified name it will be overwritten + with the new decompressed file content. **Use this option with + caution.** + + Parameters + ---------- + method : str + Name of the compression method. Can be "auto", "lzma", "xz", "bzip2", + or "gzip". + name : None or str + Defines the decompressed file name. The file name will be + ``{fname}.decomp`` if ``None`` (default) or the given name otherwise. + Note that the name should **not** include the full (or relative) path, + it should be just the file name itself. + + """ + + modules = {"auto": None, "lzma": lzma, "xz": lzma, "gzip": gzip, "bzip2": bz2} + extensions = {".xz": "lzma", ".gz": "gzip", ".bz2": "bzip2"} + + def __init__(self, method="auto", name=None): + self.method = method + self.name = name + +
[docs] def __call__(self, fname, action, pooch): + """ + Decompress the given file. + + The output file will be either ``{fname}.decomp`` or the given *name* + class attribute. + + Parameters + ---------- + fname : str + Full path of the compressed file in local storage. + action : str + Indicates what action was taken by :meth:`pooch.Pooch.fetch` or + :func:`pooch.retrieve`: + + - ``"download"``: File didn't exist locally and was downloaded + - ``"update"``: Local file was outdated and was re-download + - ``"fetch"``: File exists and is updated so it wasn't downloaded + + pooch : :class:`pooch.Pooch` + The instance of :class:`pooch.Pooch` that is calling this. + + Returns + ------- + fname : str + The full path to the decompressed file. + """ + if self.name is None: + decompressed = fname + ".decomp" + else: + decompressed = os.path.join(os.path.dirname(fname), self.name) + if action in ("update", "download") or not os.path.exists(decompressed): + get_logger().info( + "Decompressing '%s' to '%s' using method '%s'.", + fname, + decompressed, + self.method, + ) + module = self._compression_module(fname) + with open(decompressed, "w+b") as output: + with module.open(fname) as compressed: + shutil.copyfileobj(compressed, output) + return decompressed
+ + def _compression_module(self, fname): + """ + Get the Python module compatible with fname and the chosen method. + + If the *method* attribute is "auto", will select a method based on the + extension. If no recognized extension is in the file name, will raise a + ValueError. + """ + error_archives = "To unpack zip/tar archives, use pooch.Unzip/Untar instead." + if self.method not in self.modules: + message = ( + f"Invalid compression method '{self.method}'. " + f"Must be one of '{list(self.modules.keys())}'." + ) + if self.method in {"zip", "tar"}: + message = " ".join([message, error_archives]) + raise ValueError(message) + if self.method == "auto": + ext = os.path.splitext(fname)[-1] + if ext not in self.extensions: + message = ( + f"Unrecognized file extension '{ext}'. " + f"Must be one of '{list(self.extensions.keys())}'." + ) + if ext in {".zip", ".tar"}: + message = " ".join([message, error_archives]) + raise ValueError(message) + return self.modules[self.extensions[ext]] + return self.modules[self.method]
+
+ +
+ + + +
+
+ +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/_modules/pooch/utils.html b/v1.8.0/_modules/pooch/utils.html new file mode 100644 index 00000000..c636f884 --- /dev/null +++ b/v1.8.0/_modules/pooch/utils.html @@ -0,0 +1,803 @@ + + + + + + + + + + pooch.utils | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

+ +
+
+ +
+
+
+ +
+ +

Source code for pooch.utils

+# Copyright (c) 2018 The Pooch Developers.
+# Distributed under the terms of the BSD 3-Clause License.
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# This code is part of the Fatiando a Terra project (https://www.fatiando.org)
+#
+"""
+Misc utilities
+"""
+import logging
+import os
+import tempfile
+import hashlib
+from pathlib import Path
+from urllib.parse import urlsplit
+from contextlib import contextmanager
+import warnings
+
+import platformdirs
+from packaging.version import Version
+
+
+LOGGER = logging.Logger("pooch")
+LOGGER.addHandler(logging.StreamHandler())
+
+
+def file_hash(*args, **kwargs):
+    """
+    WARNING: Importing this function from pooch.utils is DEPRECATED.
+    Please import from the top-level namespace (`from pooch import file_hash`)
+    instead, which is fully backwards compatible with pooch >= 0.1.
+
+    Examples
+    --------
+
+    >>> fname = "test-file-for-hash.txt"
+    >>> with open(fname, "w") as f:
+    ...     __ = f.write("content of the file")
+    >>> print(file_hash(fname))
+    0fc74468e6a9a829f103d069aeb2bb4f8646bad58bf146bb0e3379b759ec4a00
+    >>> import os
+    >>> os.remove(fname)
+
+    """
+    # pylint: disable=import-outside-toplevel
+    from .hashes import file_hash as new_file_hash
+
+    message = """
+    Importing file_hash from pooch.utils is DEPRECATED. Please import from the
+    top-level namespace (`from pooch import file_hash`) instead, which is fully
+    backwards compatible with pooch >= 0.1.
+    """
+    warnings.warn(message, DeprecationWarning, stacklevel=2)
+    return new_file_hash(*args, **kwargs)
+
+
+
[docs]def get_logger(): + r""" + Get the default event logger. + + The logger records events like downloading files, unzipping archives, etc. + Use the method :meth:`logging.Logger.setLevel` of this object to adjust the + verbosity level from Pooch. + + Returns + ------- + logger : :class:`logging.Logger` + The logger object for Pooch + """ + return LOGGER
+ + +
[docs]def os_cache(project): + r""" + Default cache location based on the operating system. + + The folder locations are defined by the ``platformdirs`` package + using the ``user_cache_dir`` function. + Usually, the locations will be following (see the + `platformdirs documentation <https://platformdirs.readthedocs.io>`__): + + * Mac: ``~/Library/Caches/<AppName>`` + * Unix: ``~/.cache/<AppName>`` or the value of the ``XDG_CACHE_HOME`` + environment variable, if defined. + * Windows: ``C:\Users\<user>\AppData\Local\<AppAuthor>\<AppName>\Cache`` + + Parameters + ---------- + project : str + The project name. + + Returns + ------- + cache_path : :class:`pathlib.Path` + The default location for the data cache. User directories (``'~'``) are + not expanded. + + """ + return Path(platformdirs.user_cache_dir(project))
+ + +
[docs]def check_version(version, fallback="master"): + """ + Check if a version is PEP440 compliant and there are no unreleased changes. + + For example, ``version = "0.1"`` will be returned as is but ``version = + "0.1+10.8dl8dh9"`` will return the fallback. This is the convention used by + `versioneer <https://github.com/warner/python-versioneer>`__ to mark that + this version is 10 commits ahead of the last release. + + Parameters + ---------- + version : str + A version string. + fallback : str + What to return if the version string has unreleased changes. + + Returns + ------- + version : str + If *version* is PEP440 compliant and there are unreleased changes, then + return *version*. Otherwise, return *fallback*. + + Raises + ------ + InvalidVersion + If *version* is not PEP440 compliant. + + Examples + -------- + + >>> check_version("0.1") + '0.1' + >>> check_version("0.1a10") + '0.1a10' + >>> check_version("0.1+111.9hdg36") + 'master' + >>> check_version("0.1+111.9hdg36", fallback="dev") + 'dev' + + """ + parse = Version(version) + if parse.local is not None: + return fallback + return version
+ + +def parse_url(url): + """ + Parse a URL into 3 components: + + <protocol>://<netloc>/<path> + + Example URLs: + + * http://127.0.0.1:8080/test.nc + * ftp://127.0.0.1:8080/test.nc + * doi:10.6084/m9.figshare.923450.v1/test.nc + + The DOI is a special case. The protocol will be "doi", the netloc will be + the DOI, and the path is what comes after the last "/". + The only exception are Zenodo dois: the protocol will be "doi", the netloc + will be composed by the "prefix/suffix" and the path is what comes after + the second "/". This allows to support special cases of Zenodo dois where + the path contains forward slashes "/", created by the GitHub-Zenodo + integration service. + + Parameters + ---------- + url : str + The URL. + + Returns + ------- + parsed_url : dict + Three components of a URL (e.g., + ``{'protocol':'http', 'netloc':'127.0.0.1:8080','path': '/test.nc'}``). + + """ + if url.startswith("doi://"): + raise ValueError( + f"Invalid DOI link '{url}'. You must not use '//' after 'doi:'." + ) + if url.startswith("doi:"): + protocol = "doi" + parts = url[4:].split("/") + if "zenodo" in parts[1].lower(): + netloc = "/".join(parts[:2]) + path = "/" + "/".join(parts[2:]) + else: + netloc = "/".join(parts[:-1]) + path = "/" + parts[-1] + else: + parsed_url = urlsplit(url) + protocol = parsed_url.scheme or "file" + netloc = parsed_url.netloc + path = parsed_url.path + return {"protocol": protocol, "netloc": netloc, "path": path} + + +def cache_location(path, env=None, version=None): + """ + Location of the cache given a base path and optional configuration. + + Checks for the environment variable to overwrite the path of the local + cache. Optionally add *version* to the path if given. + + Parameters + ---------- + path : str, PathLike, list or tuple + The path to the local data storage folder. If this is a list or tuple, + we'll join the parts with the appropriate separator. Use + :func:`pooch.os_cache` for a sensible default. + version : str or None + The version string for your project. Will be appended to given path if + not None. + env : str or None + An environment variable that can be used to overwrite *path*. This + allows users to control where they want the data to be stored. We'll + append *version* to the end of this value as well. + + Returns + ------- + local_path : PathLike + The path to the local directory. + + """ + if env is not None and env in os.environ and os.environ[env]: + path = os.environ[env] + if isinstance(path, (list, tuple)): + path = os.path.join(*path) + if version is not None: + path = os.path.join(str(path), version) + path = os.path.expanduser(str(path)) + return Path(path) + + +def make_local_storage(path, env=None): + """ + Create the local cache directory and make sure it's writable. + + Parameters + ---------- + path : str or PathLike + The path to the local data storage folder. + env : str or None + An environment variable that can be used to overwrite *path*. Only used + in the error message in case the folder is not writable. + """ + path = str(path) + # Check that the data directory is writable + try: + if not os.path.exists(path): + action = "create" + # When running in parallel, it's possible that multiple jobs will + # try to create the path at the same time. Use exist_ok to avoid + # raising an error. + os.makedirs(path, exist_ok=True) + else: + action = "write to" + with tempfile.NamedTemporaryFile(dir=path): + pass + except PermissionError as error: + message = [ + str(error), + f"| Pooch could not {action} data cache folder '{path}'.", + "Will not be able to download data files.", + ] + if env is not None: + message.append( + f"Use environment variable '{env}' to specify a different location." + ) + raise PermissionError(" ".join(message)) from error + + +@contextmanager +def temporary_file(path=None): + """ + Create a closed and named temporary file and make sure it's cleaned up. + + Using :class:`tempfile.NamedTemporaryFile` will fail on Windows if trying + to open the file a second time (when passing its name to Pooch function, + for example). This context manager creates the file, closes it, yields the + file path, and makes sure it's deleted in the end. + + Parameters + ---------- + path : str or PathLike + The directory in which the temporary file will be created. + + Yields + ------ + fname : str + The path to the temporary file. + + """ + tmp = tempfile.NamedTemporaryFile(delete=False, dir=path) + # Close the temp file so that it can be opened elsewhere + tmp.close() + try: + yield tmp.name + finally: + if os.path.exists(tmp.name): + os.remove(tmp.name) + + +def unique_file_name(url): + """ + Create a unique file name based on the given URL. + + The file name will be unique to the URL by prepending the name with the MD5 + hash (hex digest) of the URL. The name will also include the last portion + of the URL. + + The format will be: ``{md5}-{filename}.{ext}`` + + The file name will be cropped so that the entire name (including the hash) + is less than 255 characters long (the limit on most file systems). + + Parameters + ---------- + url : str + The URL with a file name at the end. + + Returns + ------- + fname : str + The file name, unique to this URL. + + Examples + -------- + + >>> print(unique_file_name("https://www.some-server.org/2020/data.txt")) + 02ddee027ce5ebb3d7059fb23d210604-data.txt + >>> print(unique_file_name("https://www.some-server.org/2019/data.txt")) + 9780092867b497fca6fc87d8308f1025-data.txt + >>> print(unique_file_name("https://www.some-server.org/2020/data.txt.gz")) + 181a9d52e908219c2076f55145d6a344-data.txt.gz + + """ + md5 = hashlib.md5(url.encode()).hexdigest() + fname = parse_url(url)["path"].split("/")[-1] + # Crop the start of the file name to fit 255 characters including the hash + # and the : + fname = fname[-(255 - len(md5) - 1) :] + unique_name = f"{md5}-{fname}" + return unique_name +
+ +
+ + + +
+
+ +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/_panels_static/panels-main.c949a650a448cc0ae9fd3441c0e17fb0.css b/v1.8.0/_panels_static/panels-main.c949a650a448cc0ae9fd3441c0e17fb0.css new file mode 100644 index 00000000..fc14abc8 --- /dev/null +++ b/v1.8.0/_panels_static/panels-main.c949a650a448cc0ae9fd3441c0e17fb0.css @@ -0,0 +1 @@ +details.dropdown .summary-title{padding-right:3em !important;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none}details.dropdown:hover{cursor:pointer}details.dropdown .summary-content{cursor:default}details.dropdown summary{list-style:none;padding:1em}details.dropdown summary .octicon.no-title{vertical-align:middle}details.dropdown[open] summary .octicon.no-title{visibility:hidden}details.dropdown summary::-webkit-details-marker{display:none}details.dropdown summary:focus{outline:none}details.dropdown summary:hover .summary-up svg,details.dropdown summary:hover .summary-down svg{opacity:1}details.dropdown .summary-up svg,details.dropdown .summary-down svg{display:block;opacity:.6}details.dropdown .summary-up,details.dropdown .summary-down{pointer-events:none;position:absolute;right:1em;top:.75em}details.dropdown[open] .summary-down{visibility:hidden}details.dropdown:not([open]) .summary-up{visibility:hidden}details.dropdown.fade-in[open] summary~*{-moz-animation:panels-fade-in .5s ease-in-out;-webkit-animation:panels-fade-in .5s ease-in-out;animation:panels-fade-in .5s ease-in-out}details.dropdown.fade-in-slide-down[open] summary~*{-moz-animation:panels-fade-in .5s ease-in-out, panels-slide-down .5s ease-in-out;-webkit-animation:panels-fade-in .5s ease-in-out, panels-slide-down .5s ease-in-out;animation:panels-fade-in .5s ease-in-out, panels-slide-down .5s ease-in-out}@keyframes panels-fade-in{0%{opacity:0}100%{opacity:1}}@keyframes panels-slide-down{0%{transform:translate(0, -10px)}100%{transform:translate(0, 0)}}.octicon{display:inline-block;fill:currentColor;vertical-align:text-top}.tabbed-content{box-shadow:0 -.0625rem var(--tabs-color-overline),0 .0625rem var(--tabs-color-underline);display:none;order:99;padding-bottom:.75rem;padding-top:.75rem;width:100%}.tabbed-content>:first-child{margin-top:0 !important}.tabbed-content>:last-child{margin-bottom:0 !important}.tabbed-content>.tabbed-set{margin:0}.tabbed-set{border-radius:.125rem;display:flex;flex-wrap:wrap;margin:1em 0;position:relative}.tabbed-set>input{opacity:0;position:absolute}.tabbed-set>input:checked+label{border-color:var(--tabs-color-label-active);color:var(--tabs-color-label-active)}.tabbed-set>input:checked+label+.tabbed-content{display:block}.tabbed-set>input:focus+label{outline-style:auto}.tabbed-set>input:not(.focus-visible)+label{outline:none;-webkit-tap-highlight-color:transparent}.tabbed-set>label{border-bottom:.125rem solid transparent;color:var(--tabs-color-label-inactive);cursor:pointer;font-size:var(--tabs-size-label);font-weight:700;padding:1em 1.25em .5em;transition:color 250ms;width:auto;z-index:1}html .tabbed-set>label:hover{color:var(--tabs-color-label-active)} diff --git a/v1.8.0/_panels_static/panels-variables.87249bd3f7b54630a2508c1b61607c7f.css b/v1.8.0/_panels_static/panels-variables.87249bd3f7b54630a2508c1b61607c7f.css new file mode 100644 index 00000000..c613e784 --- /dev/null +++ b/v1.8.0/_panels_static/panels-variables.87249bd3f7b54630a2508c1b61607c7f.css @@ -0,0 +1,7 @@ +:root { +--tabs-color-label-active: hsla(231, 99%, 66%, 1); +--tabs-color-label-inactive: hsla(231, 99%, 66%, 0.5); +--tabs-color-overline: rgb(207, 236, 238); +--tabs-color-underline: rgb(207, 236, 238); +--tabs-size-label: 1rem; +} \ No newline at end of file diff --git a/v1.8.0/_sources/about.rst.txt b/v1.8.0/_sources/about.rst.txt new file mode 100644 index 00000000..1969234c --- /dev/null +++ b/v1.8.0/_sources/about.rst.txt @@ -0,0 +1,56 @@ +.. _about: + +Why use Pooch? +============== + +Use cases +--------- + + +.. tabbed:: Just download a file + + **Who**: Scientists/researchers/developers looking to simply download a + file. + + Pooch makes it easy to download a file (one function call). + On top of that, it also comes with some bonus features: + + * Download and cache your data files locally (so it's only downloaded + once). + * Make sure everyone running the code has the same version of the data + files by verifying cryptographic hashes. + * Multiple download protocols HTTP/FTP/SFTP and basic authentication. + * Download from Digital Object Identifiers (DOIs) issued by repositories + like figshare and Zenodo. + * Built-in utilities to unzip/decompress files upon download + + **Start here:** :ref:`retrieve` + +.. tabbed:: Use by another library + + **Who**: Package developers wanting to include sample data for use in + tutorials and tests. + + Pooch was designed for this! It offers: + + * Pure Python and :ref:`minimal dependencies `. + * Download a file only if necessary. + * Verification of download integrity through cryptographic hashes. + * Extensible design: plug in custom download and post-processing functions. + * Built-in utilities to unzip/decompress files upon download + * Multiple download protocols HTTP/FTP/SFTP and basic authentication. + * User control of data cache location through environment variables. + + **Start here:** :ref:`intermediate` + +History +------- + +Pooch was born out of shared need between the +`Fatiando a Terra `__ libraries and +`MetPy `__. +During the +`Scipy Conference 2018 `__ +sprints, developers from both projects got together and, realizing the shared +necessity, devised a package that would combine the best of the existing +functionality already present in each project and extend it's capabilities. diff --git a/v1.8.0/_sources/api/generated/pooch.DOIDownloader.rst.txt b/v1.8.0/_sources/api/generated/pooch.DOIDownloader.rst.txt new file mode 100644 index 00000000..1e18a1d8 --- /dev/null +++ b/v1.8.0/_sources/api/generated/pooch.DOIDownloader.rst.txt @@ -0,0 +1,142 @@ +pooch.DOIDownloader +=================== + +.. currentmodule:: pooch + +.. autoclass:: DOIDownloader + + .. rubric:: Methods Summary + + .. autosummary:: + + + DOIDownloader.__call__ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +---- + + + +.. automethod:: DOIDownloader.__call__ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +.. raw:: html + +
\ No newline at end of file diff --git a/v1.8.0/_sources/api/generated/pooch.Decompress.rst.txt b/v1.8.0/_sources/api/generated/pooch.Decompress.rst.txt new file mode 100644 index 00000000..9283a594 --- /dev/null +++ b/v1.8.0/_sources/api/generated/pooch.Decompress.rst.txt @@ -0,0 +1,154 @@ +pooch.Decompress +================ + +.. currentmodule:: pooch + +.. autoclass:: Decompress + + .. rubric:: Methods Summary + + .. autosummary:: + + + Decompress.__call__ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +---- + + + +.. automethod:: Decompress.__call__ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +.. raw:: html + +
\ No newline at end of file diff --git a/v1.8.0/_sources/api/generated/pooch.FTPDownloader.rst.txt b/v1.8.0/_sources/api/generated/pooch.FTPDownloader.rst.txt new file mode 100644 index 00000000..06af1ea8 --- /dev/null +++ b/v1.8.0/_sources/api/generated/pooch.FTPDownloader.rst.txt @@ -0,0 +1,142 @@ +pooch.FTPDownloader +=================== + +.. currentmodule:: pooch + +.. autoclass:: FTPDownloader + + .. rubric:: Methods Summary + + .. autosummary:: + + + FTPDownloader.__call__ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +---- + + + +.. automethod:: FTPDownloader.__call__ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +.. raw:: html + +
\ No newline at end of file diff --git a/v1.8.0/_sources/api/generated/pooch.HTTPDownloader.rst.txt b/v1.8.0/_sources/api/generated/pooch.HTTPDownloader.rst.txt new file mode 100644 index 00000000..85bb9ab5 --- /dev/null +++ b/v1.8.0/_sources/api/generated/pooch.HTTPDownloader.rst.txt @@ -0,0 +1,142 @@ +pooch.HTTPDownloader +==================== + +.. currentmodule:: pooch + +.. autoclass:: HTTPDownloader + + .. rubric:: Methods Summary + + .. autosummary:: + + + HTTPDownloader.__call__ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +---- + + + +.. automethod:: HTTPDownloader.__call__ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +.. raw:: html + +
\ No newline at end of file diff --git a/v1.8.0/_sources/api/generated/pooch.Pooch.rst.txt b/v1.8.0/_sources/api/generated/pooch.Pooch.rst.txt new file mode 100644 index 00000000..ff9ca22c --- /dev/null +++ b/v1.8.0/_sources/api/generated/pooch.Pooch.rst.txt @@ -0,0 +1,206 @@ +pooch.Pooch +=========== + +.. currentmodule:: pooch + +.. autoclass:: Pooch + + .. rubric:: Methods Summary + + .. autosummary:: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Pooch.fetch + + + + Pooch.get_url + + + + Pooch.is_available + + + + Pooch.load_registry + + + + Pooch.load_registry_from_doi + + + +---- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +.. automethod:: Pooch.fetch + + + +.. automethod:: Pooch.get_url + + + +.. automethod:: Pooch.is_available + + + +.. automethod:: Pooch.load_registry + + + +.. automethod:: Pooch.load_registry_from_doi + + + +.. raw:: html + +
\ No newline at end of file diff --git a/v1.8.0/_sources/api/generated/pooch.SFTPDownloader.rst.txt b/v1.8.0/_sources/api/generated/pooch.SFTPDownloader.rst.txt new file mode 100644 index 00000000..2bd161c4 --- /dev/null +++ b/v1.8.0/_sources/api/generated/pooch.SFTPDownloader.rst.txt @@ -0,0 +1,142 @@ +pooch.SFTPDownloader +==================== + +.. currentmodule:: pooch + +.. autoclass:: SFTPDownloader + + .. rubric:: Methods Summary + + .. autosummary:: + + + SFTPDownloader.__call__ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +---- + + + +.. automethod:: SFTPDownloader.__call__ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +.. raw:: html + +
\ No newline at end of file diff --git a/v1.8.0/_sources/api/generated/pooch.Untar.rst.txt b/v1.8.0/_sources/api/generated/pooch.Untar.rst.txt new file mode 100644 index 00000000..bebb0727 --- /dev/null +++ b/v1.8.0/_sources/api/generated/pooch.Untar.rst.txt @@ -0,0 +1,150 @@ +pooch.Untar +=========== + +.. currentmodule:: pooch + +.. autoclass:: Untar + + .. rubric:: Methods Summary + + .. autosummary:: + + + Untar.__call__ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +---- + + + +.. automethod:: Untar.__call__ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +.. raw:: html + +
\ No newline at end of file diff --git a/v1.8.0/_sources/api/generated/pooch.Unzip.rst.txt b/v1.8.0/_sources/api/generated/pooch.Unzip.rst.txt new file mode 100644 index 00000000..e61b3b45 --- /dev/null +++ b/v1.8.0/_sources/api/generated/pooch.Unzip.rst.txt @@ -0,0 +1,154 @@ +pooch.Unzip +=========== + +.. currentmodule:: pooch + +.. autoclass:: Unzip + + .. rubric:: Methods Summary + + .. autosummary:: + + + + + Unzip.__call__ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +---- + + + + + +.. automethod:: Unzip.__call__ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +.. raw:: html + +
\ No newline at end of file diff --git a/v1.8.0/_sources/api/generated/pooch.check_version.rst.txt b/v1.8.0/_sources/api/generated/pooch.check_version.rst.txt new file mode 100644 index 00000000..39784b0e --- /dev/null +++ b/v1.8.0/_sources/api/generated/pooch.check_version.rst.txt @@ -0,0 +1,10 @@ +pooch.check\_version +==================== + +.. currentmodule:: pooch + +.. autofunction:: check_version + +.. raw:: html + +
diff --git a/v1.8.0/_sources/api/generated/pooch.create.rst.txt b/v1.8.0/_sources/api/generated/pooch.create.rst.txt new file mode 100644 index 00000000..bc6ac8b6 --- /dev/null +++ b/v1.8.0/_sources/api/generated/pooch.create.rst.txt @@ -0,0 +1,10 @@ +pooch.create +============ + +.. currentmodule:: pooch + +.. autofunction:: create + +.. raw:: html + +
diff --git a/v1.8.0/_sources/api/generated/pooch.file_hash.rst.txt b/v1.8.0/_sources/api/generated/pooch.file_hash.rst.txt new file mode 100644 index 00000000..bc9756bb --- /dev/null +++ b/v1.8.0/_sources/api/generated/pooch.file_hash.rst.txt @@ -0,0 +1,10 @@ +pooch.file\_hash +================ + +.. currentmodule:: pooch + +.. autofunction:: file_hash + +.. raw:: html + +
diff --git a/v1.8.0/_sources/api/generated/pooch.get_logger.rst.txt b/v1.8.0/_sources/api/generated/pooch.get_logger.rst.txt new file mode 100644 index 00000000..875eb92d --- /dev/null +++ b/v1.8.0/_sources/api/generated/pooch.get_logger.rst.txt @@ -0,0 +1,10 @@ +pooch.get\_logger +================= + +.. currentmodule:: pooch + +.. autofunction:: get_logger + +.. raw:: html + +
diff --git a/v1.8.0/_sources/api/generated/pooch.make_registry.rst.txt b/v1.8.0/_sources/api/generated/pooch.make_registry.rst.txt new file mode 100644 index 00000000..41a65c5e --- /dev/null +++ b/v1.8.0/_sources/api/generated/pooch.make_registry.rst.txt @@ -0,0 +1,10 @@ +pooch.make\_registry +==================== + +.. currentmodule:: pooch + +.. autofunction:: make_registry + +.. raw:: html + +
diff --git a/v1.8.0/_sources/api/generated/pooch.os_cache.rst.txt b/v1.8.0/_sources/api/generated/pooch.os_cache.rst.txt new file mode 100644 index 00000000..42b51c95 --- /dev/null +++ b/v1.8.0/_sources/api/generated/pooch.os_cache.rst.txt @@ -0,0 +1,10 @@ +pooch.os\_cache +=============== + +.. currentmodule:: pooch + +.. autofunction:: os_cache + +.. raw:: html + +
diff --git a/v1.8.0/_sources/api/generated/pooch.retrieve.rst.txt b/v1.8.0/_sources/api/generated/pooch.retrieve.rst.txt new file mode 100644 index 00000000..5c74c992 --- /dev/null +++ b/v1.8.0/_sources/api/generated/pooch.retrieve.rst.txt @@ -0,0 +1,10 @@ +pooch.retrieve +============== + +.. currentmodule:: pooch + +.. autofunction:: retrieve + +.. raw:: html + +
diff --git a/v1.8.0/_sources/api/generated/pooch.test.rst.txt b/v1.8.0/_sources/api/generated/pooch.test.rst.txt new file mode 100644 index 00000000..d96bfe65 --- /dev/null +++ b/v1.8.0/_sources/api/generated/pooch.test.rst.txt @@ -0,0 +1,10 @@ +pooch.test +========== + +.. currentmodule:: pooch + +.. autofunction:: test + +.. raw:: html + +
diff --git a/v1.8.0/_sources/api/index.rst.txt b/v1.8.0/_sources/api/index.rst.txt new file mode 100644 index 00000000..a349c369 --- /dev/null +++ b/v1.8.0/_sources/api/index.rst.txt @@ -0,0 +1,66 @@ +.. _api: + +List of functions and classes (API) +=================================== + +.. note:: + + **All functions and classes should be accessed from the** :mod:`pooch` + **top-level namespace.** + + Modules inside of the :mod:`pooch` package are meant mostly for internal + organization. Please **avoid importing** directly from submodules since + functions/classes may be moved around. + +.. automodule:: pooch + +Core +---- + +.. autosummary:: + :toctree: generated/ + + pooch.create + pooch.Pooch + pooch.retrieve + +Utilities +--------- + +.. autosummary:: + :toctree: generated/ + + pooch.os_cache + pooch.make_registry + pooch.file_hash + pooch.check_version + pooch.get_logger + +Downloaders +----------- + +.. autosummary:: + :toctree: generated/ + + pooch.HTTPDownloader + pooch.FTPDownloader + pooch.SFTPDownloader + pooch.DOIDownloader + +Processors +---------- + +.. autosummary:: + :toctree: generated/ + + pooch.Unzip + pooch.Untar + pooch.Decompress + +Miscellaneous +------------- + +.. autosummary:: + :toctree: generated/ + + pooch.test diff --git a/v1.8.0/_sources/authentication.rst.txt b/v1.8.0/_sources/authentication.rst.txt new file mode 100644 index 00000000..f92fb045 --- /dev/null +++ b/v1.8.0/_sources/authentication.rst.txt @@ -0,0 +1,82 @@ +.. _authentication: + +Authentication +============== + +HTTP authentication +------------------- + +Use the :class:`~pooch.HTTPDownloader` class directly to provide login +credentials to HTTP servers that require basic authentication. For example: + +.. code:: python + + from pooch import HTTPDownloader + + + def fetch_protected_data(): + """ + Fetch a file from a server that requires authentication + """ + # Let the downloader know the login credentials + download_auth = HTTPDownloader(auth=("my_username", "my_password")) + fname = GOODBOY.fetch("some-data.csv", downloader=download_auth) + data = pandas.read_csv(fname) + return data + +It's probably not a good idea to hard-code credentials in your code. One way +around this is to ask users to set their own credentials through environment +variables. The download code could look something like so: + +.. code:: python + + import os + + + def fetch_protected_data(): + """ + Fetch a file from a server that requires authentication + """ + # Get the credentials from the user's environment + username = os.environ.get("SOMESITE_USERNAME") + password = os.environ.get("SOMESITE_PASSWORD") + # Let the downloader know the login credentials + download_auth = HTTPDownloader(auth=(username, password)) + fname = GOODBOY.fetch("some-data.csv", downloader=download_auth) + data = pandas.read_csv(fname) + return data + + +FTP/SFTP with authentication +---------------------------- + +Pooch also comes with the :class:`~pooch.FTPDownloader` and +:class:`~pooch.SFTPDownloader` downloaders that can be used +when files are distributed over FTP or SFTP (secure FTP). + +.. note:: + + To download files over SFTP, + `paramiko `__ needs to be installed. + + +Sometimes the FTP server doesn't support anonymous FTP and needs authentication +or uses a non-default port. +In these cases, pass in the downloader class explicitly (works with both FTP +and SFTP): + +.. code:: python + + import os + + + def fetch_c137(): + """ + Load the C-137 sample data as a pandas.DataFrame (over FTP this time). + """ + username = os.environ.get("MYDATASERVER_USERNAME") + password = os.environ.get("MYDATASERVER_PASSWORD") + download_ftp = pooch.FTPDownloader(username=username, password=password) + fname = GOODBOY.fetch("c137.csv", downloader=download_ftp) + data = pandas.read_csv(fname) + return data diff --git a/v1.8.0/_sources/changes.rst.txt b/v1.8.0/_sources/changes.rst.txt new file mode 100644 index 00000000..2e3fc965 --- /dev/null +++ b/v1.8.0/_sources/changes.rst.txt @@ -0,0 +1,856 @@ +.. _changes: + +Changelog +========= + +Version 1.8.0 +------------- + +*Released on: 2023/10/24* + +doi:`10.5281/zenodo.10037888 `__ + +Bug fixes: + +* Fix bug: add support for old and new Zenodo APIs (`#375 `__) + +New features: + +* Only create local data directories if necessary (`#370 `__) +* Speed up import time by lazy loading requests (`#328 `__) + +Maintenance: + +* Add support for Python 3.11 (`#348 `__) +* Only run CI cron job for the upstream repository (`#361 `__) + +Documentation: + +* Add GemGIS to list of projects using Pooch (`#349 `__) +* Fix spelling of Dataverse (`#353 `__) +* Fix grammar on retrieve documentation (`#359 `__) + + +This release contains contributions from: + +* Hugo van Kemenade +* AlexanderJuestel +* Mark Harfouche +* Philip Durbin +* Rob Luke +* Santiago Soler +* Stephan Hoyer + + +Version 1.7.0 +------------- + +*Released on: 2023/02/27* + +doi:`10.5281/zenodo.7678844 `__ + +Bug fixes: + +* Make archive extraction always take members into account (`#316 `__) +* Figshare downloaders fetch the correct version, instead of always the latest one. (`#343 `__) + +New features: + +* Allow spaces in filenames in registry files (`#315 `__) +* Refactor ``Pooch.is_available`` to use downloaders (`#322 `__) +* Add support for downloading files from Dataverse DOIs (`#318 `__) +* Add a new ``Pooch.load_registry_from_doi`` method that populates the Pooch registry using DOI-based data repositories (`#325 `__) +* Support urls for Zenodo repositories created through the GitHub integration service, which include slashes in the filename of the main zip files (`#340 `__) +* Automatically add a trailing slash to ``base_url`` on ``pooch.create`` (`#344 `__) + +Maintenance: + +* Drop support for Python 3.6 (`#299 `__) +* Port from deprecated ``appdirs`` to ``platformdirs`` (`#339 `__) +* Update version of Codecov's Action to v3 (`#345 `__) + +Documentation: + +* Update sphinx, theme, and sphinx-panels (`#300 `__) +* Add CITATION.cff for the JOSS article (`#308 `__) +* Use Markdown for the README (`#311 `__) +* Improve docstring of `known_hash` in `retrieve` function (`#333 `__) +* Replace link to Pooch's citation with a BibTeX code snippet (`#335 `__) + +Projects that started using Pooch: + +* Open AR-Sandbox (`#305 `__) +* ``climlab`` (`#312 `__) +* SciPy (`#320 `__) +* ``napari`` (`#321 `__) +* ``mne-python`` (`#323 `__) + +This release contains contributions from: + +* Alex Fikl +* Anirudh Dagar +* Björn Ludwig +* Brian Rose +* Dominic Kempf +* Florian Wellmann +* Gabriel Fu +* Kyle I S Harrington +* Leonardo Uieda +* myd7349 +* Rowan Cockett +* Santiago Soler + +Version 1.6.0 +------------- + +*Released on: 2022/01/24* + +doi:`10.5281/zenodo.5793074 `__ + +.. warning:: + + **Pooch v1.6.0 is the last release that is compatible with Python 3.6.** + +Important notes: + +* Pooch now specifies version bounds for our required dependencies and a plan for dropping support for older versions. Please revise it if you depend on Pooch. + +Enhancements: + +* Add option to disable updates on hash mismatch (`#291 `__ and `#292 `__) +* Allow enabling progress bars with an argument in ``Pooch.fetch`` and ``retrieve`` (`#277 `__) + +Documentation: + +* Use real data URLs in the README example code (`#295 `__) +* Tell users to import from the top-level namespace (`#288 `__) +* Update the contact link to `fatiando.org/contact `__ (`#282 `__) +* Refer the community guides to `fatiando/community `__ (`#281 `__) +* Mention in docs that figshare collections aren't supported (`#275 `__) + +Maintenance: + +* Replace Google Analytics for `Plausible `__ to make our docs more privacy-friendly (`#293 `__) +* Use `Dependente `__ to capture dependencies on CI (`#289 `__) +* Use ``build`` instead of setup.py (`#287 `__) +* Run the tests weekly on GitHub Actions (`#286 `__) +* Set minimum required version of dependencies (`#280 `__) +* Rename "master" to "main" throughout the project (`#278 `__) +* Remove trailing slash from GitHub handle in ``AUTHORS.md`` (`#279 `__) + +This release contains contributions from: + +* Santiago Soler +* Genevieve Buckley +* Ryan Abernathey +* Ryan May +* Leonardo Uieda + +Version 1.5.2 +------------- + +*Released on: 2021/10/11* + +doi:`10.5281/zenodo.5560923 `__ + +Bug fixes: + +* Fix bug when unpacking an entire subfolder from an archive. Now both unpacking processors (``Untar`` and ``Unzip``) handle ``members`` that are folders (not files) correctly. (`#266 `__) + +Enhancements: + +* Add support for Python 3.10 (`#260 `__) +* Point to the user's code for the file_hash warning instead of our internal code (which isn't very useful) (`#259 `__) + +Documentation: + +* Fix typo in a variable name of the examples in the documentation (`#268 `__) +* Fix typo when specifying the SFTP protocol in the about page (`#267 `__) + +Maintenance: + +* Remove old testing checks if running on TravisCI (`#265 `__) + +This release contains contributions from: + +* Santiago Soler +* Hugo van Kemenade +* Mark Harfouche +* Leonardo Uieda + +Version 1.5.1 +------------- + +*Released on: 2021/08/24* + +doi:`10.5281/zenodo.5242882 `__ + +.. warning:: + + **Please use** ``from pooch import file_hash`` **instead of** ``from + pooch.utils import file_hash``. This is backwards compatible with all + previous versions of Pooch. We recommend importing all functions and + classes from the top-level namespace. + +Bug fixes: + +* Make ``file_hash`` accessible from the ``pooch.utils`` module again. Moving + this function to ``pooch.hashes`` caused crashes downstream. To prevent these + crashes, add a wrapper back to utils that issues a warning that users should + import from the top-level namespace instead. + (`#257 `__) +* Use a mirror of the test data directory in tests that write to it. + (`#255 `__) +* Add a pytest mark for tests accessing the network so that they can easily + excluded when testing offline. (`#254 `__) + +This release contains contributions from: + +* Antonio Valentino +* Leonardo Uieda + +Version 1.5.0 +------------- + +*Released on: 2021/08/23* + +doi:`10.5281/zenodo.5235242 `__ + +New features: + +* Add support for non-cryptographic hashes from the xxhash package. They aren't + as safe (but safe enough) and compute in fractions of the time from SHA or + MD5. This makes it feasible to use hash checking on large datasets. (`#242 + `__) +* Add support for using figshare and Zenodo DOIs as URLs (with the protocol + ``doi:{DOI}/{file name}``, which works out-of-the-box with ``Pooch.fetch`` + and ``retrieve``). Can only download 1 file from the archive (not the full + archive) and the file name must be specified in the URL. (`#241 + `__) + +Maintenance: + +* Move hash functions to their own private module. No changes to the public + API. (`#244 `__) +* Run CI jobs on Python version extremes instead of all supported versions + (`#243 `__) + +This release contains contributions from: + +* Mark Harfouche +* Leonardo Uieda + +Version 1.4.0 +------------- + +*Released on: 2021/06/08* + +doi:`10.5281/zenodo.4914758 `__ + +Bug fixes: + +* Fix bug in ``Untar`` and ``Unzip`` when the archive contains subfolders + (`#224 `__) + +Documentation: + +* New theme (``sphinx-book-theme``) and layout of the documentation (`#236 + `__ `#237 + `__ `#238 + `__) + +Enhancements: + +* Add support for non-tqdm progress bars on HTTPDownloader (`#228 + `__) +* Allow custom unpack locations in ``Untar`` and ``Unzip`` (`#224 + `__) + +Maintenance: + +* Replace versioneer with setuptools-scm (`#235 + `__) +* Automatically check license notice on code files (`#231 + `__) +* Don't store documentation HTML as CI build artifacts (`#221 + `__) + +This release contains contributions from: + +* Leonardo Uieda +* Agustina Pesce +* Clément Robert +* Daniel McCloy + +Version 1.3.0 +------------- + +*Released on: 2020/11/27* + +.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.4293216.svg + :alt: Digital Object Identifier for the Zenodo archive + :target: https://doi.org/10.5281/zenodo.4293216 + +Bug fixes: + +* Properly handle capitalized hashes. On Windows, users might sometimes get + capitalized hashes from the system. To avoid false hash mismatches, convert + stored and computed hashes to lowercase before doing comparisons. Convert + hashes to lowercase when reading from the registry to make sure stored hashes + are always lowercase. (`#214 `__) + +New features: + +* Add option to retry downloads if they fail. The new ``retry_if_failed`` + option to ``pooch.create`` and ``pooch.Pooch`` allows retrying the download + the specified number of times in case of failures due to hash mismatches + (coming from Pooch) or network issues (coming from ``requests``). This is + useful for running downloads on CI that tend to fail sporadically. Waits a + period of time between consecutive downloads starting with 1s and increasing + up to 10s in 1s increments. (`#215 + `__) +* Allow user defined decompressed file names. Introduce new ``name`` argument + to ``pooch.Decompress`` to allow user defined file names. Defaults to the + previous naming convention for backward compatibility. (`#203 + `__) + +Documentation: + +* Add seaborn-image to list of packages using Pooch (`#218 + `__) + +Maintenance: + +* Add support for Python 3.9. (`#220 + `__) +* Drop support for Python 3.5. (`#204 + `__) +* Use pip instead of conda to speed up Actions (`#216 + `__) +* Add license and copyright notice to every .py file (`#213 + `__) + +This release contains contributions from: + +* Leonardo Uieda +* Danilo Horta +* Hugo van Kemenade +* SarthakJariwala + + +Version 1.2.0 +------------- + +*Released on: 2020/09/10* + +.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.4022246.svg + :alt: Digital Object Identifier for the Zenodo archive + :target: https://doi.org/10.5281/zenodo.4022246 + +.. warning:: + + **Pooch v1.2.0 is the last release that is compatible with Python 3.5.** + +Bug fixes: + +* Fix FTP availability check when the file is in a directory. If the data file + is not in the base directory, the ``Pooch.is_available`` test was broken + since we were checking for the full path in ``ftp.nlst`` instead of just the + file name. (`#191 `__) + +New features: + +* Add the SFTPDownloader class for secure FTP downloads (`#165 + `__) +* Expose Pooch version as ``pooch.__version__`` (`#179 + `__) +* Allow line comments in registry files with ``#`` (`#180 + `__) + +Enhancements: + +* Point to Unzip/tar from Decompress docs and errors (`#200 + `__) + +Documentation: + +* Re-factor the documentation into separate pages (`#202 + `__) +* Add warning to the docs about dropping Python 3.5 (`#201 + `__) +* Add `histolab `__ to the Pooch-powered + projects (`#189 `__) + +Maintenance: + +* Push documentation to GitHub Pages using Actions (`#198 + `__) +* Add GitHub Actions workflow for publishing to PyPI (`#196 + `__) +* Set up GitHub Actions for testing and linting (`#194 + `__) +* Test FTP downloads using a local test server (`#192 + `__) + +This release contains contributions from: + +* Leonardo Uieda +* Hugo van Kemenade +* Alessia Marcolini +* Luke Gregor +* Mathias Hauser + +Version 1.1.1 +------------- + +*Released on: 2020/05/14* + +.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.3826458.svg + :alt: Digital Object Identifier for the Zenodo archive + :target: https://doi.org/10.5281/zenodo.3826458 + +Bug fixes: + +* Delay data cache folder creation until the first download is attempted. As + seen in `recent issues in scikit-image + `__, creating the + data folder in ``pooch.create`` can cause problems since this function is + called at import time. This means that importing the package in parallel can + cause race conditions and crashes. To prevent that from happening, delay the + creation of the cache folder until ``Pooch.fetch`` or ``retrieve`` are + called. + (`#173 `__) +* Allow the data folder to already exist when creating it. This is can help + cope with parallel execution as well. + (`#171 `__) + +Documentation: + +* Added scikit-image to list of Pooch users. + (`#168 `__) +* Fix typo in README and front page contributing section. + (`#166 `__) + +This release contains contributions from: + +* Leonardo Uieda +* Egor Panfilov +* Rowan Cockett + +Version 1.1.0 +------------- + +*Released on: 2020/04/13* + +.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.3747184.svg + :alt: Digital Object Identifier for the Zenodo archive + :target: https://doi.org/10.5281/zenodo.3747184 + +New features: + +* New function ``pooch.retrieve`` to fetch single files This is much more + convenient than setting up a ``Pooch`` while retaining the hash checks and + use of downloaders and processors. It automatically selects a unique file + name and saves files to a cache folder. + (`#152 `__) +* Allow to use of different hashing algorithms (other than SHA256). Optionally + specify the hash as ``alg:hash`` and allow ``pooch.Pooch`` to recognize the + algorithm when comparing hashes. Setting an algorithsm is optional and + omiting it defaults to SHA256. This is particularly useful when data are + coming from external sources and published hashes are already available. + (`#133 `__) + +Documentation: + +* Add example for fetching datasets that change on the server, for which the + hash check would always fail. + (`#144 `__) +* Fix path examples in docstring of ``pooch.os_cache``. The docstring mentioned + the data path as examples instead of the cache path. + (`#140 `__) +* Add example of creating a registry when you don't have the data files locally + and would have to download them manually. The example uses the + ``pooch.retrieve`` function to automate the process. The example covers two + cases: when all remote files share the same base URL and when every file has + its own URL. + (`#161 `__) + +Maintenance: + +* A lot of general refactoring of the internals of Pooch to facilitate + development of the new ``pooch.retrieve`` function + (`#159 `__ + `#157 `__ + `#156 `__ + `#151 `__ + `#149 `__) + +This release contains contributions from: + +* Leonardo Uieda +* Santiago Soler +* Kacper Kowalik +* Lucas Martin-King +* Zac Flamig + +Version 1.0.0 +------------- + +*Released on: 2020/01/28* + +.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.3629329.svg + :alt: Digital Object Identifier for the Zenodo archive + :target: https://doi.org/10.5281/zenodo.3629329 + +This release marks the stabilization of the Pooch API. Further changes to the +1.* line will be fully backwards compatible (meaning that updating Pooch should +not break existing code). If there is great need to make backwards incompatible +changes, we will release a 2.* line. In that case, bug fixes will still be +ported to the 1.* line for a period of time. + +Improvements: + +* Allow blank lines in registry files. Previously, they would cause an error. + (`#138 `__) + +**Backwards incompatible changes**: + +* Using Python's ``logging`` module to instead of ``warnings`` to inform users + of download, update, and decompression/unpacking actions. This allows + messages to be logged with different priorities and the user filter out log + messages or silence Pooch entirely. Introduces the function + ``pooch.get_logger`` to access the ``logging`` object used by Pooch. **Users + who relied on Pooch issuing warnings will need to update to capturing logs + instead.** All other parts of the API remain unchanged. + (`#115 `__) + +This release contains contributions from: + +* Daniel Shapero + +Version 0.7.2 +------------- + +*Released on: 2020/01/17* + +🚨 **Announcement:** 🚨 +We now have a `JOSS paper about Pooch `__! +Please :ref:`cite it ` when you use Pooch for your research. +(`#116 `__ with reviews in +`#132 `__ and +`#134 `__) + +This is minor release which only updates the citation information to +the new JOSS paper. No DOI was issued for this release since there are +no code or documentation changes. + +Version 0.7.1 +------------- + +*Released on: 2020/01/17* + +.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.3611376.svg + :alt: Digital Object Identifier for the Zenodo archive + :target: https://doi.org/10.5281/zenodo.3611376 + +Improvements: + +* Better error messages when hashes don't match. Include the file name in the + exception for a hash mismatch between a downloaded file and the registry. + Before, we included the name of temporary file, which wasn't very + informative. + (`#128 `__) +* Better error message for malformed registry files. When loading a registry + file, inform the name of the file and include the offending content in the + error message instead of just the line number. + (`#129 `__) + +Maintenance: + +* Change development status flag in ``setup.py`` to "stable" instead of + "alpha". + (`#127 `__) + +This release was reviewed at the `Journal of Open Source Software +`__. The code and +software paper contain contributions from: + +* Anderson Banihirwe +* Martin Durant +* Mark Harfouche +* Hugo van Kemenade +* John Leeman +* Rémi Rampin +* Daniel Shapero +* Santiago Rubén Soler +* Matthew Turk +* Leonardo Uieda + +Version 0.7.0 +------------- + +*Released on: 2019/11/19* + +.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.3547640.svg + :alt: Digital Object Identifier for the Zenodo archive + :target: https://doi.org/10.5281/zenodo.3547640 + +New features: + +* New ``pooch.FTPDownloader`` class for downloading files over FTP. Uses the + standard library ``ftplib``. The appropriate downloader is automatically + selected by ``pooch.Pooch.fetch`` based on the URL (for anonymous FTP only), + so no configuration is required. + If authentication is required, ``pooch.FTPDownloader`` provides the need + support. Ported from + `NCAR/aletheia-data `__ by the author. + (`#118 `__) +* Support for file-like objects to ``Pooch.load_registry`` (opened either in + binary or text mode). + (`#117 `__) + +Maintenance: + +* Testing and official support for Python 3.8. + (`#113 `__) +* 🚨 **Drop support for Python 2.7.** 🚨 Remove conditional dependencies and CI + jobs. + (`#100 `__) + +Documentation: + +* In the tutorial, use ``pkg_resources.resource_stream()`` from setuptools to + load the ``registry.txt`` file. It's less error-prone than using ``os.path`` + and ``__file__`` and allows the package to work from zip files. + (`#120 `__) +* Docstrings formatted to 79 characters (instead of 88) for better rendering in + Jupyter notebooks and IPython. These displays are limited to 80 chars so the + longer lines made the docstring unreadable. + (`#123 `__) + +This release contains contributions from: + +* Anderson Banihirwe +* Hugo van Kemenade +* Remi Rampin +* Leonardo Uieda + +Version 0.6.0 +------------- + +*Released on: 2019/10/22* + +.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.3515031.svg + :alt: Digital Object Identifier for the Zenodo archive + :target: https://doi.org/10.5281/zenodo.3515031 + +🚨 **Pooch v0.6.0 is the last release to support Python 2.7** 🚨 + +New features: + +* Add optional download progress bar to ``pooch.HTTPDownloader`` + (`#97 `__) + +Maintenance: + +* Warn that 0.6.0 is the last version to support Python 2.7 + (`#108 `__) + +Documentation: + +* Update contact information to point to our Slack channel + (`#107 `__) +* Add icepack to list of projects using Pooch + (`#98 `__) + +This release contains contributions from: + +* Daniel Shapero +* Leonardo Uieda + +Version 0.5.2 +------------- + +*Released on: 2019/06/24* + +Maintenance: + +* Add back support for Python 3.5 with continuous integration tests. No code changes + were needed, only removing the restriction from ``setup.py``. + (`#93 `__) + +This release contains contributions from: + +* Leonardo Uieda + +Version 0.5.1 +------------- + +*Released on: 2019/05/21* + +Documentation fixes: + +* Fix formatting error in ``pooch.Decompress`` docstring. + (`#81 `__) +* Fix wrong imports in the usage guide for post-processing hooks. + (`#84 `__) +* Add section to the usage guide explaining when to use ``pooch.Decompress``. + (`#85 `__) + +This release contains contributions from: + +* Santiago Soler +* Leonardo Uieda + +Version 0.5.0 +------------- + +*Released on: 2019/05/20* + +New features: + +* New processor ``pooch.Decompress`` saves a decompressed version of the downloaded + file. Supports gzip, lzma/xz, and bzip2 compression. **Note**: Under Python 2.7, lzma + and bzip2 require the ``backports.lzma`` and ``bz2file`` packages as well. These are + soft dependencies and not required to use Pooch. See :ref:`install`. (`#78 + `__) +* New processor ``pooch.Untar`` unpacks files contained in a downloaded tar archive + (with or without compression). (`#77 `__) + +This release contains contributions from: + +* Matthew Turk +* Leonardo Uieda + +Version 0.4.0 +------------- + +*Released on: 2019/05/01* + +New features: + +* Add customizable downloaders. Delegate file download into separate classes that can be + passed to ``Pooch.fetch``. Created the ``HTTPDownloader`` class (used by default) + which can also be used to download files that require authentication/login. (`#66 + `__) +* Add post-download processor hooks to ``Pooch.fetch``. Allows users to pass in a + function that is executed right before returning and can overwrite the file path that + is returned by ``fetch``. Use this, for example, to perform unpacking/decompression + operations on larger files that can be time consuming and we only want to do once. + (`#59 `__) +* Add the ``Unzip`` post-download processor to extract files from a downloaded zip + archive. Unpacks files into a directory in the local store and returns a list of all + unzipped files. (`#72 `__) +* Make the ``check_version`` function public. It's used internally but will be useful in + examples that want to download things from the pooch repository. (`#69 + `__) + +Maintenance: + +* Pin sphinx to version 1.8.5. New versions of Sphinx (2.0.*) are messing up the + numpydoc style docstrings. (`#64 `__) + +This release contains contributions from: + +* Santiago Soler +* Leonardo Uieda + +Version 0.3.1 +------------- + +*Released on: 2019/03/28* + +Minor patches: + +* Add a project logo (`#57 `__) +* Replace ``http`` with ``https`` in the ``README.rst`` to avoid mixed content warnings + in some browsers (`#56 `__) + +Version 0.3.0 +------------- + +*Released on: 2019/03/27* + +New features: + +* Use the ``appdirs`` library to get the cache directory. **Could change the default + data location on all platforms**. Locations are compatible with the + `XDG Base Directory Specification `__ + (`#45 `__) +* Add method ``Pooch.is_available`` to check remote file availability + (`#50 `__) +* Add ``Pooch.registry_files`` property to get a name of all files in the registry + (`#42 `__) +* Make ``Pooch.get_url`` a public method to get the download URL for a given file + (`#55 `__) + +Maintenance: + +* **Drop support for Python 3.5**. Pooch now requires Python >= 3.6. + (`#52 `__) +* Add a private method to check if a file is in the registry (`#49 `__) +* Fix typo in the ``Pooch.load_registry`` docstring (`#41 `__) + +This release contains contributions from: + +* Santiago Soler +* Rémi Rampin +* Leonardo Uieda + +Version 0.2.1 +------------- + +*Released on: 2018/11/15* + +Bug fixes: + +* Fix unwanted ``~`` directory creation when not using a ``version`` in ``pooch.create`` + (`#37 `__) + + +Version 0.2.0 +------------- + +*Released on: 2018/10/31* + +Bug fixes: + +* Avoid copying of files across the file system (`#33 `__) +* Correctly delete temporary downloads on error (`#32 `__) + +New features: + +* Allow custom download URLs for individual files (`#30 `__) +* Allow dataset versioning to be optional (`#29 `__) + +Maintenance: + +* Move URLs building to a dedicated method for easy subclassing (`#31 `__) +* Add testing and support for Python 3.7 (`#25 `__) + + +Version 0.1.1 +------------- + +*Released on: 2018/08/30* + +Bug fixes: + +* Check if the local data folder is writable and warn the user instead of crashing + (`#23 `__) + + +Version 0.1 +----------- + +*Released on: 2018/08/20* + +* Fist release of Pooch. Manages downloading sample data files over HTTP from a server + and storing them in a local directory. Main features: + + - Download a file only if it's not in the local storage. + - Check the SHA256 hash to make sure the file is not corrupted or needs updating. + - If the hash is different from the registry, Pooch will download a new version of + the file. + - If the hash still doesn't match, Pooch will raise an exception warning of possible + data corruption. diff --git a/v1.8.0/_sources/citing.rst.txt b/v1.8.0/_sources/citing.rst.txt new file mode 100644 index 00000000..9c76d397 --- /dev/null +++ b/v1.8.0/_sources/citing.rst.txt @@ -0,0 +1,3 @@ +.. _citing: + +.. include:: ../CITATION.rst diff --git a/v1.8.0/_sources/compatibility.rst.txt b/v1.8.0/_sources/compatibility.rst.txt new file mode 100644 index 00000000..ab9e3371 --- /dev/null +++ b/v1.8.0/_sources/compatibility.rst.txt @@ -0,0 +1,71 @@ +.. _compatibility: + +Version compatibility +===================== + +Pooch backwards incompatible changes +------------------------------------ + +We try to retain backwards compatibility whenever possible. Major breaking +changes to the Pooch API will be marked by a major release and deprecation +warnings will be issued in previous releases to give developers ample time to +adapt. + +If there are any backwards incompatible changes, they will be listed below: + +.. list-table:: + :widths: 20 10 70 + + * - **Version introduced** + - **Severity** + - **Notes** + * - v1.0.0 + - Low + - We replaced use of ``warning`` with the ``logging`` module for all + messages issued by Pooch. This allows messages to be logged with + different priorities and the user filter out log messages or silence + Pooch entirely. **Users who relied on Pooch issuing warnings will need + to update to capturing logs instead.** The vast majority of users are + unaffected. + +.. _dependency-versions: + +Supported dependency versions +----------------------------- + +Pooch follows the recommendations in +`NEP29 `__ for setting +the minimum required version of our dependencies. +In short, we support **all minor releases of our dependencies from the previous +24 months** before a Pooch release with a minimum of 2 minor releases. + +We follow this guidance conservatively and won't require newer versions if the +older ones are still working without causing problems. +Whenever support for a version is dropped, we will include a note in the +:ref:`changes`. + +.. note:: + + This was introduced in Pooch v1.6.0. + + +.. _python-versions: + +Supported Python versions +------------------------- + +If you require support for older Python versions, please pin Pooch to the +following releases to ensure compatibility: + +.. list-table:: + :widths: 40 60 + + * - **Python version** + - **Last compatible Pooch release** + * - 2.7 + - 0.6.0 + * - 3.5 + - 1.2.0 + * - 3.6 + - 1.6.0 + diff --git a/v1.8.0/_sources/decompressing.rst.txt b/v1.8.0/_sources/decompressing.rst.txt new file mode 100644 index 00000000..e9f4ae86 --- /dev/null +++ b/v1.8.0/_sources/decompressing.rst.txt @@ -0,0 +1,56 @@ +.. _decompressing: + +Decompressing +============= + +If you have a compressed file that is not an archive (zip or tar), you can use +:class:`pooch.Decompress` to decompress it after download. + +For example, large binary files can be compressed with ``gzip`` to reduce +download times but will need to be decompressed before loading, which can be +slow. +You can trade storage space for speed by keeping a decompressed copy of the +file: + +.. code:: python + + from pooch import Decompress + + def fetch_compressed_file(): + """ + Load a large binary file that has been gzip compressed. + """ + # Pass in the processor to decompress the file on download + fname = GOODBOY.fetch("large-binary-file.npy.gz", processor=Decompress()) + # The file returned is the decompressed version which can be loaded by + # numpy + data = numpy.load(fname) + return data + +:class:`pooch.Decompress` returns ``"large-binary-file.npy.gz.decomp"`` as the +decompressed file name by default. +You can change this behaviour by passing a file name instead: + +.. code:: python + + import os + from pooch import Decompress + + def fetch_compressed_file(): + """ + Load a large binary file that has been gzip compressed. + """ + # Pass in the processor to decompress the file on download + fname = GOODBOY.fetch("large-binary-file.npy.gz", + processor=Decompress(name="a-different-file-name.npy"), + ) + # The file returned is now named "a-different-file-name.npy" + data = numpy.load(fname) + return data + +.. warning:: + + Passing in ``name`` can cause existing data to be lost! + For example, if a file already exists with the specified name it will be + overwritten with the new decompressed file content. + **Use this option with caution.** diff --git a/v1.8.0/_sources/downloaders.rst.txt b/v1.8.0/_sources/downloaders.rst.txt new file mode 100644 index 00000000..349fb907 --- /dev/null +++ b/v1.8.0/_sources/downloaders.rst.txt @@ -0,0 +1,126 @@ +.. _downloaders: + +Downloaders: Customizing the download +===================================== + +By default, :meth:`pooch.Pooch.fetch` and :meth:`pooch.retrieve` will detect +the download protocol from the given URL (HTTP, FTP, SFTP, DOI) and use the +appropriate download method. +Sometimes this is not enough: some servers require logins, redirections, or +other non-standard operations. +To get around this, use the ``downloader`` argument of +:meth:`~pooch.Pooch.fetch` and :meth:`~pooch.retrieve`. + +Downloaders are Python *callable objects* (like functions or classes with a +``__call__`` method) and must have the following format: + +.. code:: python + + def mydownloader(url, output_file, pooch): + ''' + Download a file from the given URL to the given local file. + + The function **must** take the following arguments (in order). + + Parameters + ---------- + url : str + The URL to the file you want to download. + output_file : str or file-like object + Path (and file name) to which the file will be downloaded. + pooch : pooch.Pooch + The instance of the Pooch class that is calling this function. + + No return value is required. + ''' + ... + +Pooch provides downloaders for HTTP, FTP, and SFTP that support authentication +and optionally printing progress bars. +See :ref:`api` for a list of available downloaders. + +Common uses of downloaders include: + +* Passing :ref:`login credentials ` to HTTP and FTP servers +* Printing :ref:`progress bars ` + + +Creating your own downloaders +----------------------------- + +If your use case is not covered by our downloaders, you can implement your own. +:meth:`pooch.Pooch.fetch` and :func:`pooch.retrieve` will accept any *callable +obejct* that has the signature specified above. As an example, consider the +case in which the login credentials need to be provided to a site that is +redirected from the original download URL: + +.. code:: python + + import requests + + + def redirect_downloader(url, output_file, pooch): + """ + Download after following a redirection. + """ + # Get the credentials from the user's environment + username = os.environ.get("SOMESITE_USERNAME") + password = os.environ.get("SOMESITE_PASSWORD") + # Make a request that will redirect to the login page + login = requests.get(url) + # Provide the credentials and download from the new URL + download = HTTPDownloader(auth=(username, password)) + download(login.url, output_file, mypooch) + + + def fetch_protected_data(): + """ + Fetch a file from a server that requires authentication + """ + fname = GOODBOY.fetch("some-data.csv", downloader=redirect_downloader) + data = pandas.read_csv(fname) + return data + + +Availability checks +------------------- + +**Optionally**, downloaders can take a ``check_only`` keyword argument (default +to ``False``) that makes them only check if a given file is available for +download **without** downloading the file. +This makes a downloader compatible with :meth:`pooch.Pooch.is_available`. + +In this case, the downloader should return a boolean: + +.. code:: python + + def mydownloader(url, output_file, pooch, check_only=False): + ''' + Download a file from the given URL to the given local file. + + The function **must** take the following arguments (in order). + + Parameters + ---------- + url : str + The URL to the file you want to download. + output_file : str or file-like object + Path (and file name) to which the file will be downloaded. + pooch : pooch.Pooch + The instance of the Pooch class that is calling this function. + check_only : bool + If True, will only check if a file exists on the server and + **without downloading the file**. Will return ``True`` if the file + exists and ``False`` otherwise. + + Returns + ------- + None or availability + If ``check_only==True``, returns a boolean indicating if the file + is available on the server. Otherwise, returns ``None``. + ''' + ... + +If a downloader does not implement an availability check (i.e., doesn't take +``check_only`` as a keyword argument), then :meth:`pooch.Pooch.is_available` +will raise a ``NotImplementedError``. diff --git a/v1.8.0/_sources/hashes.rst.txt b/v1.8.0/_sources/hashes.rst.txt new file mode 100644 index 00000000..ba30f2d0 --- /dev/null +++ b/v1.8.0/_sources/hashes.rst.txt @@ -0,0 +1,131 @@ +.. _hashes: + +Hashes: Calculating and bypassing +================================= + +Pooch uses hashes to check if files are up-to-date or possibly +corrupted: + +* If a file exists in the local folder, Pooch will check that its hash matches + the one in the registry. If it doesn't, we'll assume that it needs to be + updated. +* If a file needs to be updated or doesn't exist, Pooch will download it from + the remote source and check the hash. If the hash doesn't match, an exception + is raised to warn of possible file corruption. +* Cryptographic hashes may be used where users wish to ensure the security of + their download. + +Calculating hashes +------------------ + +You can generate hashes for your data files using ``openssl`` in the terminal: + +.. code:: bash + + $ openssl sha256 data/c137.csv + SHA256(data/c137.csv)= baee0894dba14b12085eacb204284b97e362f4f3e5a5807693cc90ef415c1b2d + +Or using the :func:`pooch.file_hash` function (which is a convenient way of +calling Python's :mod:`hashlib`): + +.. code:: python + + import pooch + print(pooch.file_hash("data/c137.csv")) + + +Specifying the hash algorithm +----------------------------- + +By default, Pooch uses `SHA256 `__ +hashes. +Other hash methods that are available in :mod:`hashlib` can also be used: + +.. code:: python + + import pooch + print(pooch.file_hash("data/c137.csv", alg="sha512")) + +In this case, you can specify the hash algorithm in the **registry** by +prepending it to the hash, for example ``"md5:0hljc7298ndo2"`` or +``"sha512:803o3uh2pecb2p3829d1bwouh9d"``. +Pooch will understand this and use the appropriate method. + + +Bypassing the hash check +------------------------ + +Sometimes we might not know the hash of the file or it could change on the +server periodically. +To bypass the check, we can set the hash value to ``None`` when specifying the +``registry`` argument for :func:`pooch.create` +(or the ``known_hash`` in :func:`pooch.retrieve`). + +In this example, we want to use Pooch to download a list of weather stations +around Australia: + +* The file with the stations is in an FTP server and we want to store it + locally in separate folders for each day that the code is run. +* The problem is that the ``stations.zip`` file is updated on the server + instead of creating a new one, so the hash check would fail. + +This is how you can solve this problem: + +.. code:: python + + import datetime + import pooch + + # Get the current data to store the files in separate folders + CURRENT_DATE = datetime.datetime.now().date() + + GOODBOY = pooch.create( + path=pooch.os_cache("bom_daily_stations") / CURRENT_DATE, + base_url="ftp://ftp.bom.gov.au/anon2/home/ncc/metadata/sitelists/", + registry={ + "stations.zip": None, + }, + ) + +When running this same code again at a different date, the file will be +downloaded again because the local cache folder changed and the file is no +longer present in it. +If you omit ``CURRENT_DATE`` from the cache path, then Pooch will only fetch +the files once, unless they are deleted from the cache. + +.. attention:: + + If this script is run over a period of time, your cache directory will + increase in size, as the files are stored in daily subdirectories. + + +.. _hashes-other: + +Other supported hashes +---------------------- + +Beyond hashing algorithms supported by ``hashlib``, Pooch supports algorithms +provided by the `xxhash package `__. +If the ``xxhash`` package is available, users may specify to use one of +the algorithms provided by the package. + +.. code:: bash + + $ xxh128sum data/store.zip + 6a71973c93eac6c8839ce751ce10ae48 data/store.zip + $ # ^^^^^^^^^^^^^^^^^^^ The hash ^^^^^^^^^^^^^^ The filename + +.. code:: python + + import datetime + import pooch + + # Get the current data to store the files in separate folders + CURRENT_DATE = datetime.datetime.now().date() + + GOODBOY = pooch.create( + [...], + registry={ + "store.zip": "xxh128:6a71973c93eac6c8839ce751ce10ae48", + }, + ) diff --git a/v1.8.0/_sources/index.rst.txt b/v1.8.0/_sources/index.rst.txt new file mode 100644 index 00000000..57b98fa5 --- /dev/null +++ b/v1.8.0/_sources/index.rst.txt @@ -0,0 +1,128 @@ +.. title:: Home + +======== +|banner| +======== + +.. |banner| image:: _static/readme-banner.png + :alt: Pooch Documentation + :align: middle + +.. raw:: html + +

+ Just want to download a file without messing with + requests and urllib? +
+ Trying to add sample datasets to your Python package? +
+ Pooch is here to help! +

+ + +.. panels:: + :header: text-center text-large + :card: border-1 m-1 text-center + + **Getting started** + ^^^^^^^^^^^^^^^^^^^ + + New to Pooch? + + .. link-button:: about + :type: ref + :text: Start here + :classes: btn-outline-primary btn-block stretched-link + + --- + + **Need help?** + ^^^^^^^^^^^^^^ + + Ask on our community channels + + .. link-button:: https://www.fatiando.org/contact + :type: url + :text: Join the conversation + :classes: btn-outline-primary btn-block stretched-link + + --- + + **Reference documentation** + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + A list of our functions and classes + + .. link-button:: api + :type: ref + :text: API reference + :classes: btn-outline-primary btn-block stretched-link + + --- + + **Using Pooch for research?** + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + Citations help support our work + + .. link-button:: citing + :type: ref + :text: Cite Pooch + :classes: btn-outline-primary btn-block stretched-link + + +.. seealso:: + + Pooch is a part of the + `Fatiando a Terra `_ project. + + +Table of contents +----------------- + +.. toctree:: + :caption: Getting Started + :maxdepth: 1 + + about.rst + install.rst + retrieve.rst + multiple-files.rst + sample-data.rst + +.. toctree:: + :caption: Training your Pooch + :maxdepth: 1 + + hashes.rst + user-defined-cache.rst + registry-files.rst + multiple-urls.rst + protocols.rst + logging.rst + downloaders.rst + processors.rst + authentication.rst + progressbars.rst + unpacking.rst + decompressing.rst + +.. toctree:: + :caption: Reference + :maxdepth: 1 + + api/index.rst + compatibility.rst + citing.rst + changes.rst + versions.rst + +.. toctree:: + :caption: Community + + Join the community + Code of Conduct + How to contribute + Source code on GitHub + Authors + Fatiando a Terra diff --git a/v1.8.0/_sources/install.rst.txt b/v1.8.0/_sources/install.rst.txt new file mode 100644 index 00000000..fb00e1a8 --- /dev/null +++ b/v1.8.0/_sources/install.rst.txt @@ -0,0 +1,67 @@ +.. _install: + +Installing +========== + +There are different ways to install Pooch: + +.. tabbed:: pip + + Using the `pip `__ package manager: + + .. code:: bash + + python -m pip install pooch + +.. tabbed:: conda + + Using the `conda `__ package manager that comes with the + Anaconda/Miniconda distribution: + + .. code:: bash + + conda install pooch --channel conda-forge + +.. tabbed:: Development version + + Using ``pip`` to install the latest **unreleased** version from GitHub + (**not recommended** in most situations): + + .. code:: bash + + python -m pip install --upgrade git+https://github.com/fatiando/pooch + +.. note:: + + The commands above should be executed in a terminal. On Windows, use the + ``cmd.exe`` or the "Anaconda Prompt" app if you're using Anaconda. + +Which Python? +------------- + +You'll need **Python >= 3.7**. See :ref:`python-versions` if you +require support for older versions. + +.. _dependencies: + +Dependencies +------------ + +The required dependencies should be installed automatically when you install +Pooch using ``conda`` or ``pip``. Optional dependencies have to be installed +manually. + +Required: + +* `platformdirs `__ +* `packaging `__ +* `requests `__ + +Optional: + +* `tqdm `__: For printing a download + progress bar. See :ref:`progressbars`. +* `paramiko `__: For SFTP downloads. See + :class:`pooch.SFTPDownloader`. +* `xxhash `__: For the faster xxHash + algorithms. See :ref:`hashes-other`. diff --git a/v1.8.0/_sources/logging.rst.txt b/v1.8.0/_sources/logging.rst.txt new file mode 100644 index 00000000..6dddbe78 --- /dev/null +++ b/v1.8.0/_sources/logging.rst.txt @@ -0,0 +1,27 @@ +.. _logging: + +Logging and verbosity +===================== + +Pooch uses the :mod:`logging` module to print messages about downloads and +:ref:`processor ` execution. + +Adjusting the logging level +--------------------------- + +Pooch will log events like downloading a new file, updating an existing one, or +unpacking an archive by printing to the terminal. +You can change how verbose these events are by getting the event logger from +pooch and changing the logging level: + +.. code:: python + + logger = pooch.get_logger() + logger.setLevel("WARNING") + +Most of the events from Pooch are logged at the info level; this code says that +you only care about warnings or errors, like inability to create the data +cache. +The event logger is a :class:`logging.Logger` object, so you can use that +class's methods to handle logging events in more sophisticated ways if you +wish. diff --git a/v1.8.0/_sources/multiple-files.rst.txt b/v1.8.0/_sources/multiple-files.rst.txt new file mode 100644 index 00000000..f8c0e42c --- /dev/null +++ b/v1.8.0/_sources/multiple-files.rst.txt @@ -0,0 +1,121 @@ +.. _beginner: + +Fetching files from a registry +============================== + +If you need to manage the download of multiple files from one or more +locations, then this section is for you! + +Setup +----- + +In the following example we'll assume that: + +1. You have several data files served from the same base URL (for example, + ``"https://www.somewebpage.org/science/data"``). +2. You know the file names and their + `hashes `__. + +We will use :func:`pooch.create` to set up our download manager: + +.. code:: python + + import pooch + + + odie = pooch.create( + # Use the default cache folder for the operating system + path=pooch.os_cache("my-project"), + base_url="https://www.somewebpage.org/science/data/", + # The registry specifies the files that can be fetched + registry={ + "temperature.csv": "sha256:19uheidhlkjdwhoiwuhc0uhcwljchw9ochwochw89dcgw9dcgwc", + "gravity-disturbance.nc": "sha256:1upodh2ioduhw9celdjhlfvhksgdwikdgcowjhcwoduchowjg8w", + }, + ) + +The return value (``odie``) is an instance of :class:`pooch.Pooch`. +It contains all of the information needed to fetch the data files in our +**registry** and store them in the specified cache folder. + +.. note:: + + The Pooch **registry** is a mapping of file names and their associated + hashes (and optionally download URLs). + +.. tip:: + + If you don't know the hash or are otherwise unable to obtain it, it is + possible to bypass the check. This is **not recommended** for general use, + only if it can't be avoided. See :ref:`hashes`. + + +.. attention:: + + You can have data files in **subdirectories** of the remote data store + (URL). + These files will be saved to the same subdirectories in the local storage + folder. + + However, the names of these files in the registry **must use Unix-style + separators** (``'/'``) **even on Windows**. + Pooch will handle the appropriate conversions. + + +Downloading files +----------------- + +To download one our data files and load it with `xarray +`__: + +.. code:: python + + import xarray as xr + + + file_path = odie.fetch("gravity-disturbance.nc") + # Standard use of xarray to load a netCDF file (.nc) + data = xr.open_dataset(file_path) + +The call to :meth:`pooch.Pooch.fetch` will check if the file already exists in +the cache folder. + +If it doesn't: + +1. The file is downloaded and saved to the cache folder. +2. The hash of the downloaded file is compared against the one stored in the + registry to make sure the file isn't corrupted. +3. The function returns the absolute path to the file on your computer. + +If it does: + +1. Check if it's hash matches the one in the registry. +2. If it does, no download happens and the file path is returned. +3. If it doesn't, the file is downloaded once more to get an updated version on + your computer. + +Why use this method? +-------------------- + +With :class:`pooch.Pooch`, you can centralize the information about the URLs, +hashes, and files in a single place. +Once the instance is created, it can be used to fetch individual files without +repeating the URL and hash everywhere. + +A good way to use this is to place the call to :func:`pooch.create` in Python +module (a ``.py`` file). +Then you can ``import`` the module in ``.py`` scripts or Jupyter notebooks and +use the instance to fetch your data. +This way, you don't need to define the URLs or hashes in multiple +scripts/notebooks. + +Customizing the download +------------------------ + +The :meth:`pooch.Pooch.fetch` method supports for all of Pooch's +:ref:`downloaders ` and :ref:`processors `. +You can use HTTP, FTP, and SFTP +(even with :ref:`authentication `), +:ref:`decompress files `, +:ref:`unpack archives `, +show :ref:`progress bars `, and more with a bit of configuration. diff --git a/v1.8.0/_sources/multiple-urls.rst.txt b/v1.8.0/_sources/multiple-urls.rst.txt new file mode 100644 index 00000000..5b82d5ee --- /dev/null +++ b/v1.8.0/_sources/multiple-urls.rst.txt @@ -0,0 +1,82 @@ +.. _multipleurls: + +Multiple download URLs +====================== + +You can set different download URLs for individual files with the ``urls`` +argument of :func:`pooch.create`. +It should be a dictionary with the file names as keys and the URLs for +downloading the files as values. + +For example, say we have a ``citadel.csv`` file that we want to download from +``https://www.some-data-hosting-site.com`` instead: + +.. code:: python + + # The basic setup is the same + POOCH = pooch.create( + path=pooch.os_cache("plumbus"), + base_url="https://github.com/rick/plumbus/raw/{version}/data/", + version=version, + version_dev="main", + registry={ + "c137.csv": "19uheidhlkjdwhoiwuhc0uhcwljchw9ochwochw89dcgw9dcgwc", + "cronen.csv": "1upodh2ioduhw9celdjhlfvhksgdwikdgcowjhcwoduchowjg8w", + # Still include the file in the registry + "citadel.csv": "893yprofwjndcwhx9c0ehp3ue9gcwoscjwdfgh923e0hwhcwiyc", + }, + # Now specify custom URLs for some of the files in the registry. + urls={ + "citadel.csv": "https://www.some-data-hosting-site.com/files/citadel.csv", + }, + ) + +When ``POOCH.fetch("citadel.csv")`` is called, the download will by from the +specified URL instead of the ``base_url``. +The file name will not be appended automatically to the URL in case you want to +change the file name in local storage. + +.. attention:: + + **Versioning of custom URLs is not supported** since they are assumed to be + data files independent of your project. + The file will **still be placed in a versioned cache folder**. + + +.. tip:: + + Custom URLs can be used along side ``base_url`` or you can omit + ``base_url`` entirely by setting it to an empty string (``base_url=""``). + **Doing so requires setting a custom URL for every file in the registry**. + +Usage with registry files +------------------------- + +You can also include custom URLs in a :ref:`registry file ` by +adding the URL for a file to end of the line (separated by a space): + +.. code-block:: none + + c137.csv 19uheidhlkjdwhoiwuhc0uhcwljchw9ochwochw89dcgw9dcgwc + cronen.csv 1upodh2ioduhw9celdjhlfvhksgdwikdgcowjhcwoduchowjg8w + citadel.csv 893yprofwjndcwhx9c0ehp3ue9gcwoscjwdfgh923e0hwhcwiyc https://www.some-data-hosting-site.com/files/citadel.csv + +:meth:`pooch.Pooch.load_registry` will automatically populate the ``urls`` +attribute. +This way, custom URLs don't need to be set in the code. +In fact, the module code doesn't change at all: + +.. code:: python + + # Define the Pooch exactly the same (urls is None by default) + POOCH = pooch.create( + path=pooch.os_cache("plumbus"), + base_url="https://github.com/rick/plumbus/raw/{version}/data/", + version=version, + version_dev="main", + registry=None, + ) + # If custom URLs are present in the registry file, they will be set + # automatically. + POOCH.load_registry(os.path.join(os.path.dirname(__file__), "registry.txt")) + diff --git a/v1.8.0/_sources/processors.rst.txt b/v1.8.0/_sources/processors.rst.txt new file mode 100644 index 00000000..4f946b90 --- /dev/null +++ b/v1.8.0/_sources/processors.rst.txt @@ -0,0 +1,134 @@ +.. _processors: + +Processors: Post-download actions +================================= + +Post-download actions sometimes need to be taken on downloaded files +(unzipping, conversion to a more efficient format, etc). +If these actions are time or memory consuming, it might be worth doing them +only once after the file is downloaded. +This is a way of trading disk space for computation time. +:meth:`pooch.Pooch.fetch` and :func:`pooch.retrieve` accept the ``processor`` +argument to handle these situations. + +Processors are Python *callable objects* (like functions or classes with a +``__call__`` method) that are executed after a file is downloaded to perform +these actions. +They must have the following format: + +.. code:: python + + def myprocessor(fname, action, pooch): + ''' + Processes the downloaded file and returns a new file name. + + The function **must** take as arguments (in order): + + fname : str + The full path of the file in the local data storage + action : str + Either: "download" (file doesn't exist and will be downloaded), + "update" (file is outdated and will be downloaded), or "fetch" + (file exists and is updated so no download is necessary). + pooch : pooch.Pooch + The instance of the Pooch class that is calling this function. + + The return value can be anything but is usually a full path to a file + (or list of files). This is what will be returned by Pooch.fetch and + pooch.retrieve in place of the original file path. + ''' + ... + return full_path + +The processor is executed after a file downloaded attempted (whether the +download actually happens or not) and before returning the path to the +downloaded file. +The processor lets us intercept the returned path, perform actions, and +possibly return a different path. + +Pooch provides built-in processors for common tasks, like decompressing files +and unpacking tar and zip archives. See the :ref:`api` for a full list. + +Common uses cases for processors include: + +* :ref:`Unpacking archives ` to load individual members +* :ref:`Decompressing ` files + + +Creating your own processors +---------------------------- + +Let's say we want to implement the :class:`pooch.Unzip` processor ourselves to +extract a single file from the archive. We could do that with the following +function: + +.. code:: python + + import os + from zipfile import ZipFile + + + def unpack(fname, action, pup): + """ + Post-processing hook to unzip a file and return the unzipped file name. + + Parameters + ---------- + fname : str + Full path of the zipped file in local storage + action : str + One of "download" (file doesn't exist and will download), + "update" (file is outdated and will download), and + "fetch" (file exists and is updated so no download). + pup : Pooch + The instance of Pooch that called the processor function. + + Returns + ------- + fname : str + The full path to the unzipped file. (Return the same fname is your + processor doesn't modify the file). + + """ + # Create a new name for the unzipped file. Appending something to the + # name is a relatively safe way of making sure there are no clashes + # with other files in the registry. + unzipped = fname + ".unzipped" + # Don't unzip if file already exists and is not being downloaded + if action in ("update", "download") or not os.path.exists(unzipped): + with ZipFile(fname, "r") as zip_file: + # Extract the data file from within the archive + with zip_file.open("actual-data-file.txt") as data_file: + # Save it to our desired file name + with open(unzipped, "wb") as output: + output.write(data_file.read()) + # Return the path of the unzipped file + return unzipped + + + def fetch_zipped_file(): + """ + Load a large zipped sample data as a pandas.DataFrame. + """ + # Pass in the processor to unzip the data file + fname = GOODBOY.fetch("zipped-data-file.zip", processor=unpack) + # fname is now the path of the unzipped file which can be loaded by + # pandas directly + data = pandas.read_csv(fname) + return data + + +Similarly, you could build any custom processor function so long as it receives +the ``fname, action, pup`` arguments. Example use cases for this would be: + +* Converting data from a download-friendly format (compressed and minimal file + size) to a more user friendly format (easy to open and fast to load into + memory). +* Add missing metadata to data from public servers. You might be using public + data that has known issues (poorly formated entries, missing metadata, etc) + which can be fixed when the file is downloaded. + +The main advantage to using a processor for these actions is that they are +performed only when the file is downloaded. A modified version of the file can +be kept on disk so that loading the file is easier. This is particularly +convenient if the processor task takes a long time to run. diff --git a/v1.8.0/_sources/progressbars.rst.txt b/v1.8.0/_sources/progressbars.rst.txt new file mode 100644 index 00000000..0cdeeb78 --- /dev/null +++ b/v1.8.0/_sources/progressbars.rst.txt @@ -0,0 +1,133 @@ +.. _progressbars: + +Printing progress bars +====================== + +.. _tqdm-progressbar: + +Using ``tqdm`` progress bars +---------------------------- + +Pooch uses `tqdm `__ to print a download progress +bar. This is turned off by default but can be enabled using +``progressbar=True`` in :func:`pooch.retrieve`: + +.. code:: python + + fname = retrieve( + url="https://some-data-server.org/a-data-file.nc", + known_hash="md5:70e2afd3fd7e336ae478b1e740a5f08e", + progressbar=True, + ) + + +The resulting progress bar will be printed to the standard error stream +(STDERR) and should look something like this: + +.. code:: + + 100%|█████████████████████████████████████████| 336/336 [...] + + +You can also do the same with :meth:`pooch.Pooch.fetch`: + +.. code:: python + + POOCH = pooch.create( + ... + ) + + fname = POOCH.fetch( + "large-data-file.h5", + progressbar=True, + ) + +Alternatively, you can pass ``progressbar=True`` directly into one of our +:ref:`downloaders `: + +.. code:: python + + # Using fetch + fname = POOCH.fetch( + "large-data-file.h5", + downloader=pooch.HTTPDownloader(progressbar=True), + ) + + # Using retrieve + fname = retrieve( + url="https://some-data-server.org/a-data-file.nc", + known_hash="md5:70e2afd3fd7e336ae478b1e740a5f08e", + downloader=pooch.HTTPDownloader(progressbar=True), + ) + +.. note:: + + ``tqdm`` is not installed by default with Pooch. You will have to install + it separately in order to use this feature. + + +.. _custom-progressbar: + +Using custom progress bars +-------------------------- + +.. note:: + + At the moment, this feature is only available for + :class:`pooch.HTTPDownloader`. + +Alternatively, you can pass an arbitrary object that behaves like a progress +that implements the ``update``, ``reset``, and ``close`` methods: + +* ``update`` should accept a single integer positional argument representing + the current completion (in bytes). +* ``reset`` and ``close`` do not take any argument beside ``self``. + +The object must also have a ``total`` attribute that can be set from outside +the class. +In other words, the custom progress bar needs to behave like a ``tqdm`` +progress bar. + +Here's a minimal working example of such a custom "progress display" class: + +.. code:: python + + import sys + + class MinimalProgressDisplay: + def __init__(self, total): + self.count = 0 + self.total = total + + def __repr__(self): + return str(self.count) + "/" + str(self.total) + + def render(self): + print(f"\r{self}", file=sys.stderr, end="") + + def update(self, i): + self.count = i + self.render() + + def reset(self): + self.count = 0 + + def close(self): + print("", file=sys.stderr) + + +An instance of this class can now be passed to an ``HTTPDownloader`` as: + +.. code:: python + + # Assuming you have a pooch.Pooch instance setup + POOCH = pooch.create( + ... + ) + + minimal_progress = MinimalProgressDisplay(total=None) + + fname = POOCH.fetch( + "large-data-file.h5", + downloader=pooch.HTTPDownloader(progressbar=minimal_progress), + ) diff --git a/v1.8.0/_sources/protocols.rst.txt b/v1.8.0/_sources/protocols.rst.txt new file mode 100644 index 00000000..94fe4564 --- /dev/null +++ b/v1.8.0/_sources/protocols.rst.txt @@ -0,0 +1,126 @@ +.. _protocols: + +Download protocols +================== + +Pooch supports the HTTP, FTP, and SFTP protocols by default. +It also includes a custom protocol for Digital Object Identifiers (DOI) from +providers like `figshare `__ and `Zenodo +`__ (see :ref:`below `). +It will **automatically detect** the correct protocol from the URL and use the +appropriate download method. + +.. note:: + + To download files over SFTP, + `paramiko `__ needs to be installed. + +For example, if our data were hosted on an FTP server, we could use the +following setup: + +.. code:: python + + POOCH = pooch.create( + path=pooch.os_cache("plumbus"), + # Use an FTP server instead of HTTP. The rest is all the same. + base_url="ftp://garage-basement.org/{version}/", + version=version, + version_dev="main", + registry={ + "c137.csv": "19uheidhlkjdwhoiwuhc0uhcwljchw9ochwochw89dcgw9dcgwc", + "cronen.csv": "1upodh2ioduhw9celdjhlfvhksgdwikdgcowjhcwoduchowjg8w", + }, + ) + + + def fetch_c137(): + """ + Load the C-137 sample data as a pandas.DataFrame (over FTP this time). + """ + fname = POOCH.fetch("c137.csv") + data = pandas.read_csv(fname) + return data + +You can even specify custom functions for the download or login credentials for +**authentication**. See :ref:`downloaders` for more information. + +.. _doidownloads: + +Digital Object Identifiers (DOIs) +--------------------------------- + +Pooch can download files stored in data repositories from the DOI by formatting +the URL as ``doi:{DOI}/{file name}``. +Notice that there are no ``//`` like in HTTP/FTP and you must specify a file +name after the DOI (separated by a ``/``). + +.. seealso:: + + For a list of supported data repositories, see + :class:`pooch.DOIDownloader`. + +For example, one of our test files (``"tiny-data.txt"``) is stored in the +figshare dataset +doi:`10.6084/m9.figshare.14763051.v1 `__. +We can could use :func:`pooch.retrieve` to download it like so: + +.. code-block:: python + + file_path = pooch.retrieve( + url="doi:10.6084/m9.figshare.14763051.v1/tiny-data.txt", + known_hash="md5:70e2afd3fd7e336ae478b1e740a5f08e", + ) + +We can also make a :class:`pooch.Pooch` with a registry stored entirely on a +figshare dataset: + +.. code-block:: python + + POOCH = pooch.create( + path=pooch.os_cache("plumbus"), + # Use the figshare DOI + base_url="doi:10.6084/m9.figshare.14763051.v1/", + registry={ + "tiny-data.txt": "md5:70e2afd3fd7e336ae478b1e740a5f08e", + "store.zip": "md5:7008231125631739b64720d1526619ae", + }, + ) + + + def fetch_tiny_data(): + """ + Load the tiny data as a numpy array. + """ + fname = POOCH.fetch("tiny-data.txt") + data = numpy.loadtxt(fname) + return data + + +.. warning:: + + A figshare DOI must point to a figshare *dataset*, not a figshare + *collection*. Collection DOIs have a ``.c.`` in them, e.g. + ``doi:10.6084/m9.figshare.c.4362224.v1``. Attempting to download files + from a figshare collection will raise an error. + See `issue #274 `__ details. + +Since this type of repositories store information about the files contained in +them, we can avoid having to manually type the registry with the file names and +their hashes. +Instead, we can use the :meth:`pooch.Pooch.load_registry_from_doi` to +automatically populate the registry: + +.. code-block:: python + + POOCH = pooch.create( + path=pooch.os_cache("plumbus"), + # Use the figshare DOI + base_url="doi:10.6084/m9.figshare.14763051.v1/", + registry=None, + ) + + # Automatically populate the registry + POOCH.load_registry_from_doi() + + # Fetch one of the files in the repository + fname = POOCH.fetch("tiny-data.txt") diff --git a/v1.8.0/_sources/registry-files.rst.txt b/v1.8.0/_sources/registry-files.rst.txt new file mode 100644 index 00000000..571a146e --- /dev/null +++ b/v1.8.0/_sources/registry-files.rst.txt @@ -0,0 +1,178 @@ +.. _registryfiles: + +Registry files +============== + +Usage +----- + +If your project has a large number of data files, it can be tedious to list +them in a dictionary. In these cases, it's better to store the file names and +hashes in a file and use :meth:`pooch.Pooch.load_registry` to read them. + +.. code:: python + + import os + import pkg_resources + + POOCH = pooch.create( + path=pooch.os_cache("plumbus"), + base_url="https://github.com/rick/plumbus/raw/{version}/data/", + version=version, + version_dev="main", + # We'll load it from a file later + registry=None, + ) + # Get registry file from package_data + registry_file = pkg_resources.resource_stream("plumbus", "registry.txt") + # Load this registry file + POOCH.load_registry(registry_file) + +In this case, the ``registry.txt`` file is in the ``plumbus/`` package +directory and should be shipped with the package (see below for instructions). +We use `pkg_resources `__ +to access the ``registry.txt``, giving it the name of our Python package. + +Registry file format +-------------------- + +Registry files are light-weight text files that specify a file's name and hash. +In our example, the contents of ``registry.txt`` are: + +.. code-block:: none + + c137.csv 19uheidhlkjdwhoiwuhc0uhcwljchw9ochwochw89dcgw9dcgwc + cronen.csv 1upodh2ioduhw9celdjhlfvhksgdwikdgcowjhcwoduchowjg8w + +A specific hashing algorithm can be enforced, if a checksum for a file is +prefixed with ``alg:``: + +.. code-block:: none + + c137.csv sha1:e32b18dab23935bc091c353b308f724f18edcb5e + cronen.csv md5:b53c08d3570b82665784cedde591a8b0 + +From Pooch v1.2.0 the registry file can also contain line comments, prepended +with a ``#``: + +.. code-block:: none + + # C-137 sample data + c137.csv 19uheidhlkjdwhoiwuhc0uhcwljchw9ochwochw89dcgw9dcgwc + # Cronenberg sample data + cronen.csv 1upodh2ioduhw9celdjhlfvhksgdwikdgcowjhcwoduchowjg8w + +.. attention:: + + Make sure you set the Pooch version in your ``setup.py`` to >=1.2.0 when + using comments as earlier versions cannot handle them: + ``install_requires = [..., "pooch>=1.2.0", ...]`` + + +Packaging registry files +------------------------ + +To make sure the registry file is shipped with your package, include the +following in your ``MANIFEST.in`` file: + +.. code-block:: none + + include plumbus/registry.txt + +And the following entry in the ``setup`` function of your ``setup.py`` file: + +.. code:: python + + setup( + ... + package_data={"plumbus": ["registry.txt"]}, + ... + ) + + +Creating a registry file +------------------------ + +If you have many data files, creating the registry and keeping it updated can +be a challenge. Function :func:`pooch.make_registry` will create a registry +file with all contents of a directory. For example, we can generate the +registry file for our fictitious project from the command-line: + +.. code:: bash + + $ python -c "import pooch; pooch.make_registry('data', 'plumbus/registry.txt')" + + +Create registry file from remote files +-------------------------------------- + +If you want to create a registry file for a large number of data files that are +available for download but you don't have their hashes or any local copies, +you must download them first. Manually downloading each file +can be tedious. However, we can automate the process using +:func:`pooch.retrieve`. Below, we'll explore two different scenarios. + +If the data files share the same base url, we can use :func:`pooch.retrieve` +to download them and then use :func:`pooch.make_registry` to create the +registry: + +.. code:: python + + import os + + # Names of the data files + filenames = ["c137.csv", "cronen.csv", "citadel.csv"] + + # Base url from which the data files can be downloaded from + base_url = "https://www.some-data-hosting-site.com/files/" + + # Create a new directory where all files will be downloaded + directory = "data_files" + os.makedirs(directory) + + # Download each data file to data_files + for fname in filenames: + path = pooch.retrieve( + url=base_url + fname, known_hash=None, fname=fname, path=directory + ) + + # Create the registry file from the downloaded data files + pooch.make_registry("data_files", "registry.txt") + +If each data file has its own url, the registry file can be manually created +after downloading each data file through :func:`pooch.retrieve`: + +.. code:: python + + import os + + # Names and urls of the data files. The file names are used for naming the + # downloaded files. These are the names that will be included in the registry. + fnames_and_urls = { + "c137.csv": "https://www.some-data-hosting-site.com/c137/data.csv", + "cronen.csv": "https://www.some-data-hosting-site.com/cronen/data.csv", + "citadel.csv": "https://www.some-data-hosting-site.com/citadel/data.csv", + } + + # Create a new directory where all files will be downloaded + directory = "data_files" + os.makedirs(directory) + + # Create a new registry file + with open("registry.txt", "w") as registry: + for fname, url in fnames_and_urls.items(): + # Download each data file to the specified directory + path = pooch.retrieve( + url=url, known_hash=None, fname=fname, path=directory + ) + # Add the name, hash, and url of the file to the new registry file + registry.write( + f"{fname} {pooch.file_hash(path)} {url}\n" + ) + +.. warning:: + + Notice that there are **no checks for download integrity** (since we don't + know the file hashes before hand). Only do this for trusted data sources + and over a secure connection. If you have access to file hashes/checksums, + **we highly recommend using them** to set the ``known_hash`` argument. diff --git a/v1.8.0/_sources/retrieve.rst.txt b/v1.8.0/_sources/retrieve.rst.txt new file mode 100644 index 00000000..7836bcd6 --- /dev/null +++ b/v1.8.0/_sources/retrieve.rst.txt @@ -0,0 +1,101 @@ +.. _retrieve: + +Retrieving a single data file +============================= + +Basic usage +----------- + +If you only want to download one or two data files, use the +:func:`pooch.retrieve` function: + +.. code-block:: python + + import pooch + + + file_path = pooch.retrieve( + # URL to one of Pooch's test files + url="https://github.com/fatiando/pooch/raw/v1.0.0/data/tiny-data.txt", + known_hash="md5:70e2afd3fd7e336ae478b1e740a5f08e", + ) + +The code above will: + +1. Check if the file from this URL already exists in Pooch's default cache + folder (see :func:`pooch.os_cache`). +2. If it doesn't, the file is downloaded and saved to the cache folder. +3. The MD5 `hash `__ + is compared against the ``known_hash`` to make sure the file isn't + corrupted. +4. The function returns the absolute path to the file on your computer. + +If the file already existed on your machine, Pooch will check if it's MD5 hash +matches the ``known_hash``: + +* If it does, no download happens and the file path is returned. +* If it doesn't, the file is downloaded once more to get an updated version on + your computer. + +Since the download happens only once, you can place this function call at the +start of your script or Jupyter notebook without having to worry about repeat +downloads. +Anyone getting a copy of your code should also get the correct data file the +first time they run it. + +.. seealso:: + + Pooch can handle multiple download protocols like HTTP, FTP, SFTP, and + even download from repositories like `figshare `__ + and `Zenodo `__ by using the DOI instead of a URL. + See :ref:`protocols`. + +.. seealso:: + + You can use **different hashes** by specifying different algorithm names: + ``sha256:XXXXXX``, ``sha1:XXXXXX``, etc. See :ref:`hashes`. + + +Unknown file hash +----------------- + +If you don't know the hash of the file, you can set ``known_hash=None`` to +bypass the check. +:func:`~pooch.retrieve` will print a log message with the SHA256 hash of the +downloaded file. +**It's highly recommended that you copy and paste this hash into your code +and use it as the** ``known_hash``. + +.. tip:: + + Setting the ``known_hash`` guarantees that the next time your code is run + (by you or someone else) the exact same file is downloaded. This helps + make the results of your code **reproducible**. + + +Customizing the download +------------------------ + +The :func:`pooch.retrieve` function supports for all of Pooch's +:ref:`downloaders ` and :ref:`processors `. +You can use HTTP, FTP, and SFTP +(even with :ref:`authentication `), +:ref:`decompress files `, +:ref:`unpack archives `, +show :ref:`progress bars `, and more with a bit of configuration. + + +When not to use ``retrieve`` +---------------------------- + +If you need to manage the download and caching of several files from one or +more sources, then you should start using the full capabilities of the +:class:`pooch.Pooch` class. +It can handle sandboxing +data for different package versions, allow users to set the download +locations, and more. + +The classic example is a **Python package that contains several sample +datasets** for use in testing and documentation. + +See :ref:`beginner` and :ref:`intermediate` to get started. diff --git a/v1.8.0/_sources/sample-data.rst.txt b/v1.8.0/_sources/sample-data.rst.txt new file mode 100644 index 00000000..e419bbb7 --- /dev/null +++ b/v1.8.0/_sources/sample-data.rst.txt @@ -0,0 +1,212 @@ +.. _intermediate: + +Manage a package's sample data +============================== + +In this section, we'll use Pooch to manage the download of a Python package's +sample datasets. + +.. note:: + + The setup will be very similar to what we saw in :ref:`beginner`. + It may be helpful to read that first. + +The problem +----------- + +In this example, we'll work with the follow assumptions: + +* You develop a Python library called ``plumbus`` for analysing data emitted by + interdimensional portals. +* You want to distribute sample data so that your users can easily try out the + library by copying and pasting from the documentation. +* You want to have a ``plumbus.datasets`` module that defines functions like + ``fetch_c137()`` that will return the data loaded as a + :class:`pandas.DataFrame` for convenient access. +* Your sample data are in a folder of your GitHub repository but you don't want + to include the data files with your source and wheel distributions because of + their size. +* You use git tags to mark releases of your project. +* Your project has a variable that defines the version string. +* The version string contains an indicator that the current commit is not a + release (like ``'v1.2.3+12.d908jdl'`` or ``'v0.1+dev'``). + +For now, let's say that this is the layout of your repository on GitHub: + +.. code-block:: none + + doc/ + ... + data/ + README.md + c137.csv + cronen.csv + plumbus/ + __init__.py + ... + datasets.py + setup.py + ... + +The sample data are stored in the ``data`` folder of your repository. + +.. seealso:: + + Pooch can handle different use cases as well, like: FTP/SFTP, authenticated + HTTP, multiple URLs, decompressing and unpacking archives, etc. + See the tutorials under "Training your Pooch" and the documentation for + :func:`pooch.create` and :func:`pooch.Pooch` for more options. + + +Basic setup +----------- + +This is what the ``plumbus/datasets.py`` file would look like: + +.. code:: python + + """ + Load sample data. + """ + import pandas + import pooch + + from . import version # The version string of your project + + + BRIAN = pooch.create( + # Use the default cache folder for the operating system + path=pooch.os_cache("plumbus"), + # The remote data is on Github + base_url="https://github.com/rick/plumbus/raw/{version}/data/", + version=version, + # If this is a development version, get the data from the "main" branch + version_dev="main", + registry={ + "c137.csv": "sha256:19uheidhlkjdwhoiwuhc0uhcwljchw9ochwochw89dcgw9dcgwc", + "cronen.csv": "sha256:1upodh2ioduhw9celdjhlfvhksgdwikdgcowjhcwoduchowjg8w", + }, + ) + + + def fetch_c137(): + """ + Load the C-137 sample data as a pandas.DataFrame. + """ + # The file will be downloaded automatically the first time this is run + # returns the file path to the downloaded file. Afterwards, Pooch finds + # it in the local cache and doesn't repeat the download. + fname = BRIAN.fetch("c137.csv") + # The "fetch" method returns the full path to the downloaded data file. + # All we need to do now is load it with our standard Python tools. + data = pandas.read_csv(fname) + return data + + + def fetch_cronen(): + """ + Load the Cronenberg sample data as a pandas.DataFrame. + """ + fname = BRIAN.fetch("cronen.csv") + data = pandas.read_csv(fname) + return data + + +The ``BRIAN`` variable captures the value returned by :func:`pooch.create`, +which is an instance of the :class:`~pooch.Pooch` class. The class contains the +data registry (files, URLs, hashes, etc) and handles downloading files from the +registry using the :meth:`~pooch.Pooch.fetch` method. +When the user calls ``plumbus.datasets.fetch_c137()`` for the first time, the +data file will be downloaded and stored in the local storage. + +.. tip:: + + We're using :func:`pooch.os_cache` to set the local folder to the default + cache location for the user's operating system. You could also provide any + other path if you prefer. + + +Versioning +---------- + +The files from different version of your project will be kept in separate +folders to make sure they don't conflict with each other. This way, you can +safely update data files while maintaining backward compatibility. For example, +if ``path=".plumbus"`` and ``version="v0.1"``, the data folder will be +``.plumbus/v0.1``. + +When your project updates, Pooch will automatically setup a separate folder for +the new data files based on the given version string. The remote URL will also +be updated. Notice that there is a format specifier ``{version}`` in the URL +that Pooch substitutes for you. + +**Versioning is optional** and can be ignored by omitting the ``version`` and +``version_dev`` arguments or setting them to ``None``. + + +Retry failed downloads +---------------------- + +When downloading data repeatedly, like in continuous integration, failures can +occur due to sporadic network outages or other factors outside of our control. +In these cases, it can be frustrating to have entire jobs fail because a single +download was not successful. + +Pooch allows you to specify a number of times to retry the download in case of +failure by setting ``retry_if_failed`` in :func:`pooch.create`. This setting +will be valid for all downloads attempted with :meth:`pooch.Pooch.fetch`. The +download can fail because the file hash doesn't match the known hash (due to a +partial download, for example) or because of network errors coming from +:mod:`requests`. Other errors (file system permission errors, etc) will still +result in a failed download. + +.. note:: + + Requires Pooch >= 1.3.0. + + +Disable file updates for testing +-------------------------------- + +Sometimes we can forget to update the hash of a file in the registry when we +change one of the existing data files. +If this happens in a pull request or any branch that is not the default, Pooch +will detect that there is a mismatch and will update the local file by +re-downloading (usually from the default development branch). +If your tests don't check the file contents exactly (which is usually not +practical), you can have tests that pass on development or continuous +integration and then fail once a pull request is merged. + +In these cases, it is better to temporarily disallow file updates so that Pooch +raises an error when the hash doesn't match (indicating that you forgot to +update it). +To do so, use the ``allow_updates`` argument in :func:`pooch.create`. +Setting this to ``False`` will mean that a hash mismatch between local file and +the registry always results in an error. + +.. tip:: + + We **do not recommend setting this permanenetly to** ``False``. Instead, + set it to the name of an environment variable that activates this + behaviour, like ``pooch.create(..., + allow_updates="MYPROJECT_ALLOW_UPDATES")``. + Then you can set ``MYPROJECT_ALLOW_UPDATES=false`` on continuous + integration or when running your tests locally. + +.. note:: + + Requires Pooch >= 1.6.0. + + +Where to go from here +--------------------- + +Pooch has more features for handling different download protocols, handling +large registries, downloading from multiple sources, and more. Check out the +tutorials under "Training your Pooch" for more information. + +Most users will also benefit from reading at least: + +* :ref:`environmentvariable` +* :ref:`hashes` +* :ref:`registryfiles` diff --git a/v1.8.0/_sources/unpacking.rst.txt b/v1.8.0/_sources/unpacking.rst.txt new file mode 100644 index 00000000..7beee837 --- /dev/null +++ b/v1.8.0/_sources/unpacking.rst.txt @@ -0,0 +1,76 @@ +.. _unpacking: + +Unpacking archives +================== + +Let's say our data file is actually a zip (or tar) archive with a collection of +files. +We may want to store an unpacked version of the archive or extract just a +single file from it. +We can do both operations with the :class:`pooch.Unzip` and +:class:`pooch.Untar` processors. + +For example, to extract a single file from a zip archive: + +.. code:: python + + from pooch import Unzip + + + def fetch_zipped_file(): + """ + Load a large zipped sample data as a pandas.DataFrame. + """ + # Extract the file "actual-data-file.txt" from the archive + unpack = Unzip(members=["actual-data-file.txt"]) + # Pass in the processor to unzip the data file + fnames = GOODBOY.fetch("zipped-data-file.zip", processor=unpack) + # Returns the paths of all extract members (in our case, only one) + fname = fnames[0] + # fname is now the path of the unzipped file ("actual-data-file.txt") + # which can be loaded by pandas directly + data = pandas.read_csv(fname) + return data + +By default, the :class:`~pooch.Unzip` processor (and similarly the +:class:`~pooch.Untar` processor) will create a new folder in the same location +as the downloaded archive file, and give it the same name as the archive file +with the suffix ``.unzip`` (or ``.untar``) appended. + +If you want to change the location of the unpacked files, you can provide a +parameter ``extract_dir`` to the processor to tell it where you want to unpack +the files: + +.. code:: python + + from pooch import Untar + + + def fetch_and_unpack_tar_file(): + """ + Unpack a file from a tar archive to a custom subdirectory in the cache. + """ + # Extract a single file from the archive, to a specific location + unpack_to_custom_dir = Untar(members=["actual-data-file.txt"], + extract_dir="custom_folder") + # Pass in the processor to untar the data file + fnames = GOODBOY.fetch("tarred-data-file.tar.gz", processor=unpack) + # Returns the paths of all extract members (in our case, only one) + fname = fnames[0] + return fname + + +To extract all files into a folder and return the path to each file, omit the +``members`` parameter: + +.. code:: python + + def fetch_zipped_archive(): + """ + Load all files from a zipped archive. + """ + fnames = GOODBOY.fetch("zipped-archive.zip", processor=Unzip()) + return fnames + +Use :class:`pooch.Untar` to do the exact same for tar archives (with optional +compression). diff --git a/v1.8.0/_sources/user-defined-cache.rst.txt b/v1.8.0/_sources/user-defined-cache.rst.txt new file mode 100644 index 00000000..aec558f4 --- /dev/null +++ b/v1.8.0/_sources/user-defined-cache.rst.txt @@ -0,0 +1,34 @@ +.. _environmentvariable: + +User-defined cache location +--------------------------- + +The location of the local storage cache in the users' computer +is usually hard-coded when we call :func:`pooch.create`. +There is no way for them to change it to something else. + +To avoid being a tyrant, you can allow the user to define the cache location +using an environment variable: + +.. code:: python + + BRIAN = pooch.create( + # This is still the default + path=pooch.os_cache("plumbus"), + base_url="https://github.com/rick/plumbus/raw/{version}/data/", + version=version, + version_dev="main", + registry={ + "c137.csv": "19uheidhlkjdwhoiwuhc0uhcwljchw9ochwochw89dcgw9dcgwc", + "cronen.csv": "1upodh2ioduhw9celdjhlfvhksgdwikdgcowjhcwoduchowjg8w", + }, + # The name of an environment variable that can overwrite the path + env="PLUMBUS_DATA_DIR", + ) + +In this case, if the user defines the ``PLUMBUS_DATA_DIR`` environment +variable, Pooch use its value instead of ``path``. + +Pooch will still append the value of ``version`` to the path, so the value of +``PLUMBUS_DATA_DIR`` should not include a version number. + diff --git a/v1.8.0/_sources/versions.rst.txt b/v1.8.0/_sources/versions.rst.txt new file mode 100644 index 00000000..c6da8e44 --- /dev/null +++ b/v1.8.0/_sources/versions.rst.txt @@ -0,0 +1,35 @@ +Documentation for other versions +-------------------------------- + +Use the links below to access documentation for specific versions +(when in doubt, use the **latest release**): + +* `Latest release `__ +* `Development `__ + (reflects the current development branch on GitHub) +* `v1.8.0 `__ +* `v1.7.0 `__ +* `v1.6.0 `__ +* `v1.5.2 `__ +* `v1.5.1 `__ +* `v1.5.0 `__ +* `v1.4.0 `__ +* `v1.3.0 `__ +* `v1.2.0 `__ +* `v1.1.1 `__ +* `v1.1.0 `__ +* `v1.0.0 `__ +* `v0.7.1 `__ +* `v0.7.0 `__ +* `v0.6.0 `__ +* `v0.5.2 `__ +* `v0.5.1 `__ +* `v0.5.0 `__ +* `v0.4.0 `__ +* `v0.3.1 `__ +* `v0.3.0 `__ +* `v0.2.1 `__ +* `v0.2.0 `__ +* `v0.1.1 `__ +* `v0.1 `__ + diff --git a/v1.8.0/_static/__init__.py b/v1.8.0/_static/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/v1.8.0/_static/__pycache__/__init__.cpython-311.pyc b/v1.8.0/_static/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..001dcdc8114966c14d1a36e12c673823d75e0120 GIT binary patch literal 213 zcmXwzO$x#=5QP(qB0}*TF6R}f6ZzQs3h z-Ynic!*D^IuAjZ|^X)N;{!Kq~=0P%YAuD=%k$U3x-X8}E+8{`6QDmrfk@8duIJYQu z1>0z|iDGEt9TX}7^GkgQR-srMp62`}ELbB|)v!eCoS_t@0E-+|3XG{rA!Ctghxwt@ Z*}V|Qdg_Y(Yj)|-1(ec`jHm;beF4w{H{k#P literal 0 HcmV?d00001 diff --git a/v1.8.0/_static/banner.svg b/v1.8.0/_static/banner.svg new file mode 100644 index 00000000..923c147d --- /dev/null +++ b/v1.8.0/_static/banner.svg @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/v1.8.0/_static/basic.css b/v1.8.0/_static/basic.css new file mode 100644 index 00000000..d54be806 --- /dev/null +++ b/v1.8.0/_static/basic.css @@ -0,0 +1,906 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 270px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 450px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a.brackets:before, +span.brackets > a:before{ + content: "["; +} + +a.brackets:after, +span.brackets > a:after { + content: "]"; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +table.footnote td, table.footnote th { + border: 0 !important; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +dl.footnote > dt, +dl.citation > dt { + float: left; + margin-right: 0.5em; +} + +dl.footnote > dd, +dl.citation > dd { + margin-bottom: 0em; +} + +dl.footnote > dd:after, +dl.citation > dd:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dt:after { + content: ":"; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/v1.8.0/_static/css/blank.css b/v1.8.0/_static/css/blank.css new file mode 100644 index 00000000..8a686ec7 --- /dev/null +++ b/v1.8.0/_static/css/blank.css @@ -0,0 +1,2 @@ +/* This file is intentionally left blank to override the stylesheet of the +parent theme via theme.conf. The parent style we import directly in theme.css */ \ No newline at end of file diff --git a/v1.8.0/_static/css/index.ff1ffe594081f20da1ef19478df9384b.css b/v1.8.0/_static/css/index.ff1ffe594081f20da1ef19478df9384b.css new file mode 100644 index 00000000..9b1c5d79 --- /dev/null +++ b/v1.8.0/_static/css/index.ff1ffe594081f20da1ef19478df9384b.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap v4.5.0 (https://getbootstrap.com/) + * Copyright 2011-2020 The Bootstrap Authors + * Copyright 2011-2020 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:540px;--breakpoint-md:720px;--breakpoint-lg:960px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,:after,:before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-size:1rem;line-height:1.5;color:#212529;text-align:left}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;text-decoration:underline dotted;cursor:help;border-bottom:0;text-decoration-skip-ink:none}address{font-style:normal;line-height:inherit}address,dl,ol,ul{margin-bottom:1rem}dl,ol,ul{margin-top:0}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;background-color:transparent}a:hover{color:#0056b3}a:not([href]),a:not([href]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{border-style:none}img,svg{vertical-align:middle}svg{overflow:hidden}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem}.display-1,.display-2{font-weight:300;line-height:1.2}.display-2{font-size:5.5rem}.display-3{font-size:4.5rem}.display-3,.display-4{font-weight:300;line-height:1.2}.display-4{font-size:3.5rem}hr{margin-top:1rem;margin-bottom:1rem;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-inline,.list-unstyled{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer:before{content:"\2014\00A0"}.img-fluid,.img-thumbnail{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:540px){.container{max-width:540px}}@media (min-width:720px){.container{max-width:720px}}@media (min-width:960px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1400px}}.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:540px){.container,.container-sm{max-width:540px}}@media (min-width:720px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:960px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1400px}}.row{display:flex;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-10,.col-11,.col-12,.col-auto,.col-lg,.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-auto,.col-md,.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12,.col-md-auto,.col-sm,.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{flex-basis:0;flex-grow:1;min-width:0;max-width:100%}.row-cols-1>*{flex:0 0 100%;max-width:100%}.row-cols-2>*{flex:0 0 50%;max-width:50%}.row-cols-3>*{flex:0 0 33.33333%;max-width:33.33333%}.row-cols-4>*{flex:0 0 25%;max-width:25%}.row-cols-5>*{flex:0 0 20%;max-width:20%}.row-cols-6>*{flex:0 0 16.66667%;max-width:16.66667%}.col-auto{flex:0 0 auto;width:auto;max-width:100%}.col-1{flex:0 0 8.33333%;max-width:8.33333%}.col-2{flex:0 0 16.66667%;max-width:16.66667%}.col-3{flex:0 0 25%;max-width:25%}.col-4{flex:0 0 33.33333%;max-width:33.33333%}.col-5{flex:0 0 41.66667%;max-width:41.66667%}.col-6{flex:0 0 50%;max-width:50%}.col-7{flex:0 0 58.33333%;max-width:58.33333%}.col-8{flex:0 0 66.66667%;max-width:66.66667%}.col-9{flex:0 0 75%;max-width:75%}.col-10{flex:0 0 83.33333%;max-width:83.33333%}.col-11{flex:0 0 91.66667%;max-width:91.66667%}.col-12{flex:0 0 100%;max-width:100%}.order-first{order:-1}.order-last{order:13}.order-0{order:0}.order-1{order:1}.order-2{order:2}.order-3{order:3}.order-4{order:4}.order-5{order:5}.order-6{order:6}.order-7{order:7}.order-8{order:8}.order-9{order:9}.order-10{order:10}.order-11{order:11}.order-12{order:12}.offset-1{margin-left:8.33333%}.offset-2{margin-left:16.66667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333%}.offset-5{margin-left:41.66667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333%}.offset-8{margin-left:66.66667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333%}.offset-11{margin-left:91.66667%}@media (min-width:540px){.col-sm{flex-basis:0;flex-grow:1;min-width:0;max-width:100%}.row-cols-sm-1>*{flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{flex:0 0 33.33333%;max-width:33.33333%}.row-cols-sm-4>*{flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{flex:0 0 16.66667%;max-width:16.66667%}.col-sm-auto{flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{flex:0 0 8.33333%;max-width:8.33333%}.col-sm-2{flex:0 0 16.66667%;max-width:16.66667%}.col-sm-3{flex:0 0 25%;max-width:25%}.col-sm-4{flex:0 0 33.33333%;max-width:33.33333%}.col-sm-5{flex:0 0 41.66667%;max-width:41.66667%}.col-sm-6{flex:0 0 50%;max-width:50%}.col-sm-7{flex:0 0 58.33333%;max-width:58.33333%}.col-sm-8{flex:0 0 66.66667%;max-width:66.66667%}.col-sm-9{flex:0 0 75%;max-width:75%}.col-sm-10{flex:0 0 83.33333%;max-width:83.33333%}.col-sm-11{flex:0 0 91.66667%;max-width:91.66667%}.col-sm-12{flex:0 0 100%;max-width:100%}.order-sm-first{order:-1}.order-sm-last{order:13}.order-sm-0{order:0}.order-sm-1{order:1}.order-sm-2{order:2}.order-sm-3{order:3}.order-sm-4{order:4}.order-sm-5{order:5}.order-sm-6{order:6}.order-sm-7{order:7}.order-sm-8{order:8}.order-sm-9{order:9}.order-sm-10{order:10}.order-sm-11{order:11}.order-sm-12{order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333%}.offset-sm-2{margin-left:16.66667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333%}.offset-sm-5{margin-left:41.66667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333%}.offset-sm-8{margin-left:66.66667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333%}.offset-sm-11{margin-left:91.66667%}}@media (min-width:720px){.col-md{flex-basis:0;flex-grow:1;min-width:0;max-width:100%}.row-cols-md-1>*{flex:0 0 100%;max-width:100%}.row-cols-md-2>*{flex:0 0 50%;max-width:50%}.row-cols-md-3>*{flex:0 0 33.33333%;max-width:33.33333%}.row-cols-md-4>*{flex:0 0 25%;max-width:25%}.row-cols-md-5>*{flex:0 0 20%;max-width:20%}.row-cols-md-6>*{flex:0 0 16.66667%;max-width:16.66667%}.col-md-auto{flex:0 0 auto;width:auto;max-width:100%}.col-md-1{flex:0 0 8.33333%;max-width:8.33333%}.col-md-2{flex:0 0 16.66667%;max-width:16.66667%}.col-md-3{flex:0 0 25%;max-width:25%}.col-md-4{flex:0 0 33.33333%;max-width:33.33333%}.col-md-5{flex:0 0 41.66667%;max-width:41.66667%}.col-md-6{flex:0 0 50%;max-width:50%}.col-md-7{flex:0 0 58.33333%;max-width:58.33333%}.col-md-8{flex:0 0 66.66667%;max-width:66.66667%}.col-md-9{flex:0 0 75%;max-width:75%}.col-md-10{flex:0 0 83.33333%;max-width:83.33333%}.col-md-11{flex:0 0 91.66667%;max-width:91.66667%}.col-md-12{flex:0 0 100%;max-width:100%}.order-md-first{order:-1}.order-md-last{order:13}.order-md-0{order:0}.order-md-1{order:1}.order-md-2{order:2}.order-md-3{order:3}.order-md-4{order:4}.order-md-5{order:5}.order-md-6{order:6}.order-md-7{order:7}.order-md-8{order:8}.order-md-9{order:9}.order-md-10{order:10}.order-md-11{order:11}.order-md-12{order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333%}.offset-md-2{margin-left:16.66667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333%}.offset-md-5{margin-left:41.66667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333%}.offset-md-8{margin-left:66.66667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333%}.offset-md-11{margin-left:91.66667%}}@media (min-width:960px){.col-lg{flex-basis:0;flex-grow:1;min-width:0;max-width:100%}.row-cols-lg-1>*{flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{flex:0 0 33.33333%;max-width:33.33333%}.row-cols-lg-4>*{flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{flex:0 0 16.66667%;max-width:16.66667%}.col-lg-auto{flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{flex:0 0 8.33333%;max-width:8.33333%}.col-lg-2{flex:0 0 16.66667%;max-width:16.66667%}.col-lg-3{flex:0 0 25%;max-width:25%}.col-lg-4{flex:0 0 33.33333%;max-width:33.33333%}.col-lg-5{flex:0 0 41.66667%;max-width:41.66667%}.col-lg-6{flex:0 0 50%;max-width:50%}.col-lg-7{flex:0 0 58.33333%;max-width:58.33333%}.col-lg-8{flex:0 0 66.66667%;max-width:66.66667%}.col-lg-9{flex:0 0 75%;max-width:75%}.col-lg-10{flex:0 0 83.33333%;max-width:83.33333%}.col-lg-11{flex:0 0 91.66667%;max-width:91.66667%}.col-lg-12{flex:0 0 100%;max-width:100%}.order-lg-first{order:-1}.order-lg-last{order:13}.order-lg-0{order:0}.order-lg-1{order:1}.order-lg-2{order:2}.order-lg-3{order:3}.order-lg-4{order:4}.order-lg-5{order:5}.order-lg-6{order:6}.order-lg-7{order:7}.order-lg-8{order:8}.order-lg-9{order:9}.order-lg-10{order:10}.order-lg-11{order:11}.order-lg-12{order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333%}.offset-lg-2{margin-left:16.66667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333%}.offset-lg-5{margin-left:41.66667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333%}.offset-lg-8{margin-left:66.66667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333%}.offset-lg-11{margin-left:91.66667%}}@media (min-width:1200px){.col-xl{flex-basis:0;flex-grow:1;min-width:0;max-width:100%}.row-cols-xl-1>*{flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{flex:0 0 33.33333%;max-width:33.33333%}.row-cols-xl-4>*{flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{flex:0 0 16.66667%;max-width:16.66667%}.col-xl-auto{flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{flex:0 0 8.33333%;max-width:8.33333%}.col-xl-2{flex:0 0 16.66667%;max-width:16.66667%}.col-xl-3{flex:0 0 25%;max-width:25%}.col-xl-4{flex:0 0 33.33333%;max-width:33.33333%}.col-xl-5{flex:0 0 41.66667%;max-width:41.66667%}.col-xl-6{flex:0 0 50%;max-width:50%}.col-xl-7{flex:0 0 58.33333%;max-width:58.33333%}.col-xl-8{flex:0 0 66.66667%;max-width:66.66667%}.col-xl-9{flex:0 0 75%;max-width:75%}.col-xl-10{flex:0 0 83.33333%;max-width:83.33333%}.col-xl-11{flex:0 0 91.66667%;max-width:91.66667%}.col-xl-12{flex:0 0 100%;max-width:100%}.order-xl-first{order:-1}.order-xl-last{order:13}.order-xl-0{order:0}.order-xl-1{order:1}.order-xl-2{order:2}.order-xl-3{order:3}.order-xl-4{order:4}.order-xl-5{order:5}.order-xl-6{order:6}.order-xl-7{order:7}.order-xl-8{order:8}.order-xl-9{order:9}.order-xl-10{order:10}.order-xl-11{order:11}.order-xl-12{order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333%}.offset-xl-2{margin-left:16.66667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333%}.offset-xl-5{margin-left:41.66667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333%}.offset-xl-8{margin-left:66.66667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333%}.offset-xl-11{margin-left:91.66667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered,.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover,.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover,.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover,.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover,.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover,.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover,.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover,.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover,.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th,.table-hover .table-active:hover,.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:hsla(0,0%,100%,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:hsla(0,0%,100%,.075)}@media (max-width:539.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:719.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:959.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{appearance:none}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;font-size:1rem;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size],textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:flex;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:inline-flex;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3E%3C/svg%3E") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label:before,.was-validated .custom-control-input:valid~.custom-control-label:before{border-color:#28a745}.custom-control-input.is-valid:checked~.custom-control-label:before,.was-validated .custom-control-input:valid:checked~.custom-control-label:before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label:before,.was-validated .custom-control-input:valid:focus~.custom-control-label:before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label:before,.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label:before,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545'%3E%3Ccircle cx='6' cy='6' r='4.5'/%3E%3Cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3E%3Ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545'%3E%3Ccircle cx='6' cy='6' r='4.5'/%3E%3Cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3E%3Ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3E%3C/svg%3E") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label:before,.was-validated .custom-control-input:invalid~.custom-control-label:before{border-color:#dc3545}.custom-control-input.is-invalid:checked~.custom-control-label:before,.was-validated .custom-control-input:invalid:checked~.custom-control-label:before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label:before,.was-validated .custom-control-input:invalid:focus~.custom-control-label:before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label:before,.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label:before,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:flex;flex-flow:row wrap;align-items:center}.form-inline .form-check{width:100%}@media (min-width:540px){.form-inline label{justify-content:center}.form-inline .form-group,.form-inline label{display:flex;align-items:center;margin-bottom:0}.form-inline .form-group{flex:0 0 auto;flex-flow:row wrap}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:flex;align-items:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{align-items:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary.focus,.btn-primary:focus,.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary.focus,.btn-secondary:focus,.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success.focus,.btn-success:focus,.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info.focus,.btn-info:focus,.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning.focus,.btn-warning:focus,.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger.focus,.btn-danger:focus,.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light.focus,.btn-light:focus,.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark.focus,.btn-dark:focus,.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3}.btn-link.focus,.btn-link:focus,.btn-link:hover{text-decoration:underline}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle:after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty:after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:540px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:720px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:960px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle:after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty:after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle:after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty:after{margin-left:0}.dropright .dropdown-toggle:after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle:after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";display:none}.dropleft .dropdown-toggle:before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty:after{margin-left:0}.dropleft .dropdown-toggle:before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split:after,.dropright .dropdown-toggle-split:after,.dropup .dropdown-toggle-split:after{margin-left:0}.dropleft .dropdown-toggle-split:before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio],.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;flex:1 1 auto;width:1%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:flex;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label:after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.25rem;opacity:0}.custom-control-input:checked~.custom-control-label:before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label:before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label:before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label:before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label:before,.custom-control-input[disabled]~.custom-control-label:before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label:before{pointer-events:none;background-color:#fff;border:1px solid #adb5bd}.custom-control-label:after,.custom-control-label:before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:""}.custom-control-label:after{background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label:before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label:before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label:before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label:before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label:before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label:after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label:after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label:after{background-color:#fff;transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right .75rem center/8px 10px;border:1px solid #ced4da;border-radius:.25rem;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{display:inline-block;margin-bottom:0}.custom-file,.custom-file-input{position:relative;width:100%;height:calc(1.5em + .75rem + 2px)}.custom-file-input{z-index:2;margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label:after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]:after{content:attr(data-browse)}.custom-file-label{left:0;z-index:1;height:calc(1.5em + .75rem + 2px);font-weight:400;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label,.custom-file-label:after{position:absolute;top:0;right:0;padding:.375rem .75rem;line-height:1.5;color:#495057}.custom-file-label:after{bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;appearance:none}.custom-range:focus{outline:none}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower,.custom-range::-ms-fill-upper{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label:before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label:before,.custom-file-label,.custom-select{transition:none}}.nav{display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{flex:1 1 auto;text-align:center}.nav-justified .nav-item{flex-basis:0;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;padding:.5rem 1rem}.navbar,.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat 50%;background-size:100% 100%}@media (max-width:539.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width:540px){.navbar-expand-sm{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:719.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width:720px){.navbar-expand-md{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:959.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width:960px){.navbar-expand-lg{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand,.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30'%3E%3Cpath stroke='rgba(0,0,0,0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a,.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand,.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:hsla(0,0%,100%,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:hsla(0,0%,100%,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:hsla(0,0%,100%,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:hsla(0,0%,100%,.5);border-color:hsla(0,0%,100%,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30'%3E%3Cpath stroke='rgba(255,255,255,0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-dark .navbar-text{color:hsla(0,0%,100%,.5)}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:flex;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-body{flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem}.card-subtitle,.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-bottom:-.75rem;border-bottom:0}.card-header-pills,.card-header-tabs{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img,.card-img-bottom,.card-img-top{flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width:540px){.card-deck{display:flex;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:540px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:540px){.card-columns{column-count:3;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb,.breadcrumb-item{display:flex}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item:before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover:before{text-decoration:underline;text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:540px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@keyframes progress-bar-stripes{0%{background-position:1rem 0}to{background-position:0 0}}.progress{height:1rem;line-height:0;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress,.progress-bar{display:flex;overflow:hidden}.progress-bar{flex-direction:column;justify-content:center;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,hsla(0,0%,100%,.15) 25%,transparent 0,transparent 50%,hsla(0,0%,100%,.15) 0,hsla(0,0%,100%,.15) 75%,transparent 0,transparent);background-size:1rem 1rem}.progress-bar-animated{animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{animation:none}}.media{display:flex;align-items:flex-start}.media-body{flex:1}.list-group{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:540px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:720px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:960px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:hsla(0,0%,100%,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);backdrop-filter:blur(10px);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:flex;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:hsla(0,0%,100%,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translateY(-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered:before{display:block;height:calc(100vh - 1rem);height:min-content;content:""}.modal-dialog-centered.modal-dialog-scrollable{flex-direction:column;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable:before{content:none}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:flex;align-items:flex-start;justify-content:space-between;padding:1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;flex:1 1 auto;padding:1rem}.modal-footer{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:540px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered:before{height:calc(100vh - 3.5rem);height:min-content}.modal-sm{max-width:300px}}@media (min-width:960px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow:before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow:before,.bs-tooltip-top .arrow:before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow:before,.bs-tooltip-right .arrow:before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.bs-tooltip-bottom .arrow:before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow:before,.bs-tooltip-left .arrow:before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{top:0;left:0;z-index:1060;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover,.popover .arrow{position:absolute;display:block}.popover .arrow{width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow:after,.popover .arrow:before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow:before,.bs-popover-top>.arrow:before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow:after,.bs-popover-top>.arrow:after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow:before,.bs-popover-right>.arrow:before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow:after,.bs-popover-right>.arrow:after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow:before,.bs-popover-bottom>.arrow:before{top:0;border-width:0 .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow:after,.bs-popover-bottom>.arrow:after{top:1px;border-width:0 .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header:before,.bs-popover-bottom .popover-header:before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow:before,.bs-popover-left>.arrow:before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow:after,.bs-popover-left>.arrow:after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner:after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3E%3C/svg%3E")}.carousel-control-next-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8'%3E%3Cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3E%3C/svg%3E")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:flex;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@keyframes spinner-border{to{transform:rotate(1turn)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid;border-right:.25em solid transparent;border-radius:50%;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important}.rounded-right,.rounded-top{border-top-right-radius:.25rem!important}.rounded-bottom,.rounded-right{border-bottom-right-radius:.25rem!important}.rounded-bottom,.rounded-left{border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix:after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}@media (min-width:540px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}}@media (min-width:720px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}}@media (min-width:960px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive:before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9:before{padding-top:42.85714%}.embed-responsive-16by9:before{padding-top:56.25%}.embed-responsive-4by3:before{padding-top:75%}.embed-responsive-1by1:before{padding-top:100%}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-fill{flex:1 1 auto!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}@media (min-width:540px){.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}}@media (min-width:720px){.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}}@media (min-width:960px){.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:540px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:720px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:960px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.user-select-all{user-select:all!important}.user-select-auto{user-select:auto!important}.user-select-none{user-select:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:sticky!important}.fixed-top{top:0}.fixed-bottom,.fixed-top{position:fixed;right:0;left:0;z-index:1030}.fixed-bottom{bottom:0}@supports (position:sticky){.sticky-top{position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:540px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:720px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:960px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.stretched-link:after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:transparent}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:540px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:720px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:960px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:hsla(0,0%,100%,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,:after,:before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]:after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}.container,body{min-width:960px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}}html{font-size:var(--pst-font-size-base);scroll-padding-top:calc(var(--pst-header-height) + 12px)}body{padding-top:calc(var(--pst-header-height) + 20px);background-color:#fff;font-family:var(--pst-font-family-base);font-weight:400;line-height:1.65;color:rgba(var(--pst-color-text-base),1)}p{margin-bottom:1.15rem;font-size:1em;color:rgba(var(--pst-color-paragraph),1)}p.rubric{border-bottom:1px solid #c9c9c9}a{color:rgba(var(--pst-color-link),1);text-decoration:none}a:hover{color:rgba(var(--pst-color-link-hover),1);text-decoration:underline}a.headerlink{color:rgba(var(--pst-color-headerlink),1);font-size:.8em;padding:0 4px;text-decoration:none}a.headerlink:hover{background-color:rgba(var(--pst-color-headerlink),1);color:rgba(var(--pst-color-headerlink-hover),1)}.heading-style,h1,h2,h3,h4,h5,h6{margin:2.75rem 0 1.05rem;font-family:var(--pst-font-family-heading);font-weight:400;line-height:1.15}h1{margin-top:0;font-size:var(--pst-font-size-h1);color:rgba(var(--pst-color-h1),1)}h2{font-size:var(--pst-font-size-h2);color:rgba(var(--pst-color-h2),1)}h3{font-size:var(--pst-font-size-h3);color:rgba(var(--pst-color-h3),1)}h4{font-size:var(--pst-font-size-h4);color:rgba(var(--pst-color-h4),1)}h5{font-size:var(--pst-font-size-h5);color:rgba(var(--pst-color-h5),1)}h6{font-size:var(--pst-font-size-h6);color:rgba(var(--pst-color-h6),1)}.text_small,small{font-size:var(--pst-font-size-milli)}hr{border:0;border-top:1px solid #e5e5e5}code,kbd,pre,samp{font-family:var(--pst-font-family-monospace)}code{color:rgba(var(--pst-color-inline-code),1)}pre{margin:1.5em 0;padding:10px;background-color:rgba(var(--pst-color-preformatted-background),1);color:rgba(var(--pst-color-preformatted-text),1);line-height:1.2em;border:1px solid #c9c9c9;border-radius:.2rem;box-shadow:1px 1px 1px #d8d8d8}dd{margin-top:3px;margin-bottom:10px;margin-left:30px}.navbar{position:fixed;min-height:var(--pst-header-height);width:100%;padding:0}.navbar .container-xl{height:100%}@media (min-width:960px){.navbar #navbar-end>.navbar-end-item{display:inline-block}}.navbar-brand{position:relative;height:var(--pst-header-height);width:auto;padding:.5rem 0}.navbar-brand img{max-width:100%;height:100%;width:auto}.navbar-light{background:#fff!important;box-shadow:0 .125rem .25rem 0 rgba(0,0,0,.11)}.navbar-light .navbar-nav li a.nav-link{padding:0 .5rem;color:rgba(var(--pst-color-navbar-link),1)}.navbar-light .navbar-nav li a.nav-link:hover{color:rgba(var(--pst-color-navbar-link-hover),1)}.navbar-light .navbar-nav>.active>.nav-link{font-weight:600;color:rgba(var(--pst-color-navbar-link-active),1)}.navbar-header a{padding:0 15px}.admonition,div.admonition{margin:1.5625em auto;padding:0 .6rem .8rem;overflow:hidden;page-break-inside:avoid;border-left:.2rem solid;border-left-color:rgba(var(--pst-color-admonition-default),1);border-bottom-color:rgba(var(--pst-color-admonition-default),1);border-right-color:rgba(var(--pst-color-admonition-default),1);border-top-color:rgba(var(--pst-color-admonition-default),1);border-radius:.2rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1);transition:color .25s,background-color .25s,border-color .25s}.admonition :last-child,div.admonition :last-child{margin-bottom:0}.admonition p.admonition-title~*,div.admonition p.admonition-title~*{padding:0 1.4rem}.admonition>ol,.admonition>ul,div.admonition>ol,div.admonition>ul{margin-left:1em}.admonition>.admonition-title,div.admonition>.admonition-title{position:relative;margin:0 -.6rem;padding:.4rem .6rem .4rem 2rem;font-weight:700;background-color:rgba(var(--pst-color-admonition-default),.1)}.admonition>.admonition-title:before,div.admonition>.admonition-title:before{position:absolute;left:.6rem;width:1rem;height:1rem;color:rgba(var(--pst-color-admonition-default),1);font-family:Font Awesome\ 5 Free;font-weight:900;content:var(--pst-icon-admonition-default)}.admonition>.admonition-title+*,div.admonition>.admonition-title+*{margin-top:.4em}.admonition.attention,div.admonition.attention{border-color:rgba(var(--pst-color-admonition-attention),1)}.admonition.attention>.admonition-title,div.admonition.attention>.admonition-title{background-color:rgba(var(--pst-color-admonition-attention),.1)}.admonition.attention>.admonition-title:before,div.admonition.attention>.admonition-title:before{color:rgba(var(--pst-color-admonition-attention),1);content:var(--pst-icon-admonition-attention)}.admonition.caution,div.admonition.caution{border-color:rgba(var(--pst-color-admonition-caution),1)}.admonition.caution>.admonition-title,div.admonition.caution>.admonition-title{background-color:rgba(var(--pst-color-admonition-caution),.1)}.admonition.caution>.admonition-title:before,div.admonition.caution>.admonition-title:before{color:rgba(var(--pst-color-admonition-caution),1);content:var(--pst-icon-admonition-caution)}.admonition.warning,div.admonition.warning{border-color:rgba(var(--pst-color-admonition-warning),1)}.admonition.warning>.admonition-title,div.admonition.warning>.admonition-title{background-color:rgba(var(--pst-color-admonition-warning),.1)}.admonition.warning>.admonition-title:before,div.admonition.warning>.admonition-title:before{color:rgba(var(--pst-color-admonition-warning),1);content:var(--pst-icon-admonition-warning)}.admonition.danger,div.admonition.danger{border-color:rgba(var(--pst-color-admonition-danger),1)}.admonition.danger>.admonition-title,div.admonition.danger>.admonition-title{background-color:rgba(var(--pst-color-admonition-danger),.1)}.admonition.danger>.admonition-title:before,div.admonition.danger>.admonition-title:before{color:rgba(var(--pst-color-admonition-danger),1);content:var(--pst-icon-admonition-danger)}.admonition.error,div.admonition.error{border-color:rgba(var(--pst-color-admonition-error),1)}.admonition.error>.admonition-title,div.admonition.error>.admonition-title{background-color:rgba(var(--pst-color-admonition-error),.1)}.admonition.error>.admonition-title:before,div.admonition.error>.admonition-title:before{color:rgba(var(--pst-color-admonition-error),1);content:var(--pst-icon-admonition-error)}.admonition.hint,div.admonition.hint{border-color:rgba(var(--pst-color-admonition-hint),1)}.admonition.hint>.admonition-title,div.admonition.hint>.admonition-title{background-color:rgba(var(--pst-color-admonition-hint),.1)}.admonition.hint>.admonition-title:before,div.admonition.hint>.admonition-title:before{color:rgba(var(--pst-color-admonition-hint),1);content:var(--pst-icon-admonition-hint)}.admonition.tip,div.admonition.tip{border-color:rgba(var(--pst-color-admonition-tip),1)}.admonition.tip>.admonition-title,div.admonition.tip>.admonition-title{background-color:rgba(var(--pst-color-admonition-tip),.1)}.admonition.tip>.admonition-title:before,div.admonition.tip>.admonition-title:before{color:rgba(var(--pst-color-admonition-tip),1);content:var(--pst-icon-admonition-tip)}.admonition.important,div.admonition.important{border-color:rgba(var(--pst-color-admonition-important),1)}.admonition.important>.admonition-title,div.admonition.important>.admonition-title{background-color:rgba(var(--pst-color-admonition-important),.1)}.admonition.important>.admonition-title:before,div.admonition.important>.admonition-title:before{color:rgba(var(--pst-color-admonition-important),1);content:var(--pst-icon-admonition-important)}.admonition.note,div.admonition.note{border-color:rgba(var(--pst-color-admonition-note),1)}.admonition.note>.admonition-title,div.admonition.note>.admonition-title{background-color:rgba(var(--pst-color-admonition-note),.1)}.admonition.note>.admonition-title:before,div.admonition.note>.admonition-title:before{color:rgba(var(--pst-color-admonition-note),1);content:var(--pst-icon-admonition-note)}table.field-list{border-collapse:separate;border-spacing:10px;margin-left:1px}table.field-list th.field-name{padding:1px 8px 1px 5px;white-space:nowrap;background-color:#eee}table.field-list td.field-body p{font-style:italic}table.field-list td.field-body p>strong{font-style:normal}table.field-list td.field-body blockquote{border-left:none;margin:0 0 .3em;padding-left:30px}.table.autosummary td:first-child{white-space:nowrap}.sig{font-family:var(--pst-font-family-monospace)}.sig-inline.c-texpr,.sig-inline.cpp-texpr{font-family:unset}.sig.c .k,.sig.c .kt,.sig.c .m,.sig.c .s,.sig.c .sc,.sig.cpp .k,.sig.cpp .kt,.sig.cpp .m,.sig.cpp .s,.sig.cpp .sc{color:rgba(var(--pst-color-text-base),1)}.sig-name{color:rgba(var(--pst-color-inline-code),1)}blockquote{padding:0 1em;color:#6a737d;border-left:.25em solid #dfe2e5}dt.label>span.brackets:not(:only-child):before{content:"["}dt.label>span.brackets:not(:only-child):after{content:"]"}a.footnote-reference{vertical-align:super;font-size:small}div.deprecated{margin-bottom:10px;margin-top:10px;padding:7px;background-color:#f3e5e5;border:1px solid #eed3d7;border-radius:.5rem}div.deprecated p{color:#b94a48;display:inline}.topic{background-color:#eee}.seealso dd{margin-top:0;margin-bottom:0}.viewcode-back{font-family:var(--pst-font-family-base)}.viewcode-block:target{background-color:#f4debf;border-top:1px solid #ac9;border-bottom:1px solid #ac9}span.guilabel{border:1px solid #7fbbe3;background:#e7f2fa;font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}footer{width:100%;border-top:1px solid #ccc;padding:10px}footer .footer-item p{margin-bottom:0}.bd-search{position:relative;padding:1rem 15px;margin-right:-15px;margin-left:-15px}.bd-search .icon{position:absolute;color:#a4a6a7;left:25px;top:25px}.bd-search input{border-radius:0;border:0;border-bottom:1px solid #e5e5e5;padding-left:35px}.bd-toc{-ms-flex-order:2;order:2;height:calc(100vh - 2rem);overflow-y:auto}@supports (position:-webkit-sticky) or (position:sticky){.bd-toc{position:-webkit-sticky;position:sticky;top:calc(var(--pst-header-height) + 20px);height:calc(100vh - 5rem);overflow-y:auto}}.bd-toc .onthispage{color:#a4a6a7}.section-nav{padding-left:0;border-left:1px solid #eee;border-bottom:none}.section-nav ul{padding-left:1rem}.toc-entry,.toc-entry a{display:block}.toc-entry a{padding:.125rem 1.5rem;color:rgba(var(--pst-color-toc-link),1)}@media (min-width:1200px){.toc-entry a{padding-right:0}}.toc-entry a:hover{color:rgba(var(--pst-color-toc-link-hover),1);text-decoration:none}.bd-sidebar{padding-top:1em}@media (min-width:720px){.bd-sidebar{border-right:1px solid rgba(0,0,0,.1)}@supports (position:-webkit-sticky) or (position:sticky){.bd-sidebar{position:-webkit-sticky;position:sticky;top:calc(var(--pst-header-height) + 20px);z-index:1000;height:calc(100vh - var(--pst-header-height) - 20px)}}}.bd-sidebar.no-sidebar{border-right:0}.bd-links{padding-top:1rem;padding-bottom:1rem;margin-right:-15px;margin-left:-15px}@media (min-width:720px){.bd-links{display:block}@supports (position:-webkit-sticky) or (position:sticky){.bd-links{max-height:calc(100vh - 11rem);overflow-y:auto}}}.bd-sidenav{display:none}.bd-content{padding-top:20px}.bd-content .section{max-width:100%}.bd-content .section table{display:block;overflow:auto}.bd-toc-link{display:block;padding:.25rem 1.5rem;font-weight:600;color:rgba(0,0,0,.65)}.bd-toc-link:hover{color:rgba(0,0,0,.85);text-decoration:none}.bd-toc-item.active{margin-bottom:1rem}.bd-toc-item.active:not(:first-child){margin-top:1rem}.bd-toc-item.active>.bd-toc-link{color:rgba(0,0,0,.85)}.bd-toc-item.active>.bd-toc-link:hover{background-color:transparent}.bd-toc-item.active>.bd-sidenav{display:block}nav.bd-links p.caption{font-size:var(--pst-sidebar-caption-font-size);text-transform:uppercase;font-weight:700;position:relative;margin-top:1.25em;margin-bottom:.5em;padding:0 1.5rem;color:rgba(var(--pst-color-sidebar-caption),1)}nav.bd-links p.caption:first-child{margin-top:0}.bd-sidebar .nav{font-size:var(--pst-sidebar-font-size)}.bd-sidebar .nav ul{list-style:none;padding:0 0 0 1.5rem}.bd-sidebar .nav li>a{display:block;padding:.25rem 1.5rem;color:rgba(var(--pst-color-sidebar-link),1)}.bd-sidebar .nav li>a:hover{color:rgba(var(--pst-color-sidebar-link-hover),1);text-decoration:none;background-color:transparent}.bd-sidebar .nav li>a.reference.external:after{font-family:Font Awesome\ 5 Free;font-weight:900;content:"\f35d";font-size:.75em;margin-left:.3em}.bd-sidebar .nav .active:hover>a,.bd-sidebar .nav .active>a{font-weight:600;color:rgba(var(--pst-color-sidebar-link-active),1)}.toc-h2{font-size:.85rem}.toc-h3{font-size:.75rem}.toc-h4{font-size:.65rem}.toc-entry>.nav-link.active{font-weight:600;color:#130654;color:rgba(var(--pst-color-toc-link-active),1);background-color:transparent;border-left:2px solid rgba(var(--pst-color-toc-link-active),1)}.nav-link:hover{border-style:none}#navbar-main-elements li.nav-item i{font-size:.7rem;padding-left:2px;vertical-align:middle}.bd-toc .nav .nav{display:none}.bd-toc .nav .nav.visible,.bd-toc .nav>.active>ul{display:block}.prev-next-area{margin:20px 0}.prev-next-area p{margin:0 .3em;line-height:1.3em}.prev-next-area i{font-size:1.2em}.prev-next-area a{display:flex;align-items:center;border:none;padding:10px;max-width:45%;overflow-x:hidden;color:rgba(0,0,0,.65);text-decoration:none}.prev-next-area a p.prev-next-title{color:rgba(var(--pst-color-link),1);font-weight:600;font-size:1.1em}.prev-next-area a:hover p.prev-next-title{text-decoration:underline}.prev-next-area a .prev-next-info{flex-direction:column;margin:0 .5em}.prev-next-area a .prev-next-info .prev-next-subtitle{text-transform:capitalize}.prev-next-area a.left-prev{float:left}.prev-next-area a.right-next{float:right}.prev-next-area a.right-next div.prev-next-info{text-align:right}.alert{padding-bottom:0}.alert-info a{color:#e83e8c}#navbar-icon-links i.fa,#navbar-icon-links i.fab,#navbar-icon-links i.far,#navbar-icon-links i.fas{vertical-align:middle;font-style:normal;font-size:1.5rem;line-height:1.25}#navbar-icon-links i.fa-github-square:before{color:#333}#navbar-icon-links i.fa-twitter-square:before{color:#55acee}#navbar-icon-links i.fa-gitlab:before{color:#548}#navbar-icon-links i.fa-bitbucket:before{color:#0052cc}.tocsection{border-left:1px solid #eee;padding:.3rem 1.5rem}.tocsection i{padding-right:.5rem}.editthispage{padding-top:2rem}.editthispage a{color:var(--pst-color-sidebar-link-active)}.xr-wrap[hidden]{display:block!important}.toctree-checkbox{position:absolute;display:none}.toctree-checkbox~ul{display:none}.toctree-checkbox~label i{transform:rotate(0deg)}.toctree-checkbox:checked~ul{display:block}.toctree-checkbox:checked~label i{transform:rotate(180deg)}.bd-sidebar li{position:relative}.bd-sidebar label{position:absolute;top:0;right:0;height:30px;width:30px;cursor:pointer;display:flex;justify-content:center;align-items:center}.bd-sidebar label:hover{background:rgba(var(--pst-color-sidebar-expander-background-hover),1)}.bd-sidebar label i{display:inline-block;font-size:.75rem;text-align:center}.bd-sidebar label i:hover{color:rgba(var(--pst-color-sidebar-link-hover),1)}.bd-sidebar li.has-children>.reference{padding-right:30px}div.doctest>div.highlight span.gp,span.linenos,table.highlighttable td.linenos{user-select:none;-webkit-user-select:text;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.docutils.container{padding-left:unset;padding-right:unset} \ No newline at end of file diff --git a/v1.8.0/_static/css/theme.css b/v1.8.0/_static/css/theme.css new file mode 100644 index 00000000..2e03fe37 --- /dev/null +++ b/v1.8.0/_static/css/theme.css @@ -0,0 +1,120 @@ +/* Provided by the Sphinx base theme template at build time */ +@import "../basic.css"; + +:root { + /***************************************************************************** + * Theme config + **/ + --pst-header-height: 60px; + + /***************************************************************************** + * Font size + **/ + --pst-font-size-base: 15px; /* base font size - applied at body / html level */ + + /* heading font sizes */ + --pst-font-size-h1: 36px; + --pst-font-size-h2: 32px; + --pst-font-size-h3: 26px; + --pst-font-size-h4: 21px; + --pst-font-size-h5: 18px; + --pst-font-size-h6: 16px; + + /* smaller then heading font sizes*/ + --pst-font-size-milli: 12px; + + --pst-sidebar-font-size: .9em; + --pst-sidebar-caption-font-size: .9em; + + /***************************************************************************** + * Font family + **/ + /* These are adapted from https://systemfontstack.com/ */ + --pst-font-family-base-system: -apple-system, BlinkMacSystemFont, Segoe UI, "Helvetica Neue", + Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol; + --pst-font-family-monospace-system: "SFMono-Regular", Menlo, Consolas, Monaco, + Liberation Mono, Lucida Console, monospace; + + --pst-font-family-base: var(--pst-font-family-base-system); + --pst-font-family-heading: var(--pst-font-family-base); + --pst-font-family-monospace: var(--pst-font-family-monospace-system); + + /***************************************************************************** + * Color + * + * Colors are defined in rgb string way, "red, green, blue" + **/ + --pst-color-primary: 19, 6, 84; + --pst-color-success: 40, 167, 69; + --pst-color-info: 0, 123, 255; /*23, 162, 184;*/ + --pst-color-warning: 255, 193, 7; + --pst-color-danger: 220, 53, 69; + --pst-color-text-base: 51, 51, 51; + + --pst-color-h1: var(--pst-color-primary); + --pst-color-h2: var(--pst-color-primary); + --pst-color-h3: var(--pst-color-text-base); + --pst-color-h4: var(--pst-color-text-base); + --pst-color-h5: var(--pst-color-text-base); + --pst-color-h6: var(--pst-color-text-base); + --pst-color-paragraph: var(--pst-color-text-base); + --pst-color-link: 0, 91, 129; + --pst-color-link-hover: 227, 46, 0; + --pst-color-headerlink: 198, 15, 15; + --pst-color-headerlink-hover: 255, 255, 255; + --pst-color-preformatted-text: 34, 34, 34; + --pst-color-preformatted-background: 250, 250, 250; + --pst-color-inline-code: 232, 62, 140; + + --pst-color-active-navigation: 19, 6, 84; + --pst-color-navbar-link: 77, 77, 77; + --pst-color-navbar-link-hover: var(--pst-color-active-navigation); + --pst-color-navbar-link-active: var(--pst-color-active-navigation); + --pst-color-sidebar-link: 77, 77, 77; + --pst-color-sidebar-link-hover: var(--pst-color-active-navigation); + --pst-color-sidebar-link-active: var(--pst-color-active-navigation); + --pst-color-sidebar-expander-background-hover: 244, 244, 244; + --pst-color-sidebar-caption: 77, 77, 77; + --pst-color-toc-link: 119, 117, 122; + --pst-color-toc-link-hover: var(--pst-color-active-navigation); + --pst-color-toc-link-active: var(--pst-color-active-navigation); + + /***************************************************************************** + * Icon + **/ + + /* font awesome icons*/ + --pst-icon-check-circle: '\f058'; + --pst-icon-info-circle: '\f05a'; + --pst-icon-exclamation-triangle: '\f071'; + --pst-icon-exclamation-circle: '\f06a'; + --pst-icon-times-circle: '\f057'; + --pst-icon-lightbulb: '\f0eb'; + + /***************************************************************************** + * Admonitions + **/ + + --pst-color-admonition-default: var(--pst-color-info); + --pst-color-admonition-note: var(--pst-color-info); + --pst-color-admonition-attention: var(--pst-color-warning); + --pst-color-admonition-caution: var(--pst-color-warning); + --pst-color-admonition-warning: var(--pst-color-warning); + --pst-color-admonition-danger: var(--pst-color-danger); + --pst-color-admonition-error: var(--pst-color-danger); + --pst-color-admonition-hint: var(--pst-color-success); + --pst-color-admonition-tip: var(--pst-color-success); + --pst-color-admonition-important: var(--pst-color-success); + + --pst-icon-admonition-default: var(--pst-icon-info-circle); + --pst-icon-admonition-note: var(--pst-icon-info-circle); + --pst-icon-admonition-attention: var(--pst-icon-exclamation-circle); + --pst-icon-admonition-caution: var(--pst-icon-exclamation-triangle); + --pst-icon-admonition-warning: var(--pst-icon-exclamation-triangle); + --pst-icon-admonition-danger: var(--pst-icon-exclamation-triangle); + --pst-icon-admonition-error: var(--pst-icon-times-circle); + --pst-icon-admonition-hint: var(--pst-icon-lightbulb); + --pst-icon-admonition-tip: var(--pst-icon-lightbulb); + --pst-icon-admonition-important: var(--pst-icon-exclamation-circle); + +} diff --git a/v1.8.0/_static/doctools.js b/v1.8.0/_static/doctools.js new file mode 100644 index 00000000..e509e483 --- /dev/null +++ b/v1.8.0/_static/doctools.js @@ -0,0 +1,326 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for all documentation. + * + * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/** + * select a different prefix for underscore + */ +$u = _.noConflict(); + +/** + * make the code below compatible with browsers without + * an installed firebug like debugger +if (!window.console || !console.firebug) { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", + "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", + "profile", "profileEnd"]; + window.console = {}; + for (var i = 0; i < names.length; ++i) + window.console[names[i]] = function() {}; +} + */ + +/** + * small helper function to urldecode strings + * + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL + */ +jQuery.urldecode = function(x) { + if (!x) { + return x + } + return decodeURIComponent(x.replace(/\+/g, ' ')); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} + +/** + * Small JavaScript module for the documentation. + */ +var Documentation = { + + init : function() { + this.fixFirefoxAnchorBug(); + this.highlightSearchWords(); + this.initIndexTable(); + if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) { + this.initOnKeyListeners(); + } + }, + + /** + * i18n support + */ + TRANSLATIONS : {}, + PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; }, + LOCALE : 'unknown', + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext : function(string) { + var translated = Documentation.TRANSLATIONS[string]; + if (typeof translated === 'undefined') + return string; + return (typeof translated === 'string') ? translated : translated[0]; + }, + + ngettext : function(singular, plural, n) { + var translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated === 'undefined') + return (n == 1) ? singular : plural; + return translated[Documentation.PLURALEXPR(n)]; + }, + + addTranslations : function(catalog) { + for (var key in catalog.messages) + this.TRANSLATIONS[key] = catalog.messages[key]; + this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); + this.LOCALE = catalog.locale; + }, + + /** + * add context elements like header anchor links + */ + addContextElements : function() { + $('div[id] > :header:first').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this headline')). + appendTo(this); + }); + $('dt[id]').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this definition')). + appendTo(this); + }); + }, + + /** + * workaround a firefox stupidity + * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 + */ + fixFirefoxAnchorBug : function() { + if (document.location.hash && $.browser.mozilla) + window.setTimeout(function() { + document.location.href += ''; + }, 10); + }, + + /** + * highlight the search words provided in the url in the text + */ + highlightSearchWords : function() { + var params = $.getQueryParameters(); + var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; + if (terms.length) { + var body = $('div.body'); + if (!body.length) { + body = $('body'); + } + window.setTimeout(function() { + $.each(terms, function() { + body.highlightText(this.toLowerCase(), 'highlighted'); + }); + }, 10); + $('') + .appendTo($('#searchbox')); + } + }, + + /** + * init the domain index toggle buttons + */ + initIndexTable : function() { + var togglers = $('img.toggler').click(function() { + var src = $(this).attr('src'); + var idnum = $(this).attr('id').substr(7); + $('tr.cg-' + idnum).toggle(); + if (src.substr(-9) === 'minus.png') + $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); + else + $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); + }).css('display', ''); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { + togglers.click(); + } + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords : function() { + $('#searchbox .highlight-link').fadeOut(300); + $('span.highlighted').removeClass('highlighted'); + var url = new URL(window.location); + url.searchParams.delete('highlight'); + window.history.replaceState({}, '', url); + }, + + /** + * make the url absolute + */ + makeURL : function(relativeURL) { + return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; + }, + + /** + * get the current relative url + */ + getCurrentURL : function() { + var path = document.location.pathname; + var parts = path.split(/\//); + $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { + if (this === '..') + parts.pop(); + }); + var url = parts.join('/'); + return path.substring(url.lastIndexOf('/') + 1, path.length - 1); + }, + + initOnKeyListeners: function() { + $(document).keydown(function(event) { + var activeElementType = document.activeElement.tagName; + // don't navigate when in search box, textarea, dropdown or button + if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT' + && activeElementType !== 'BUTTON' && !event.altKey && !event.ctrlKey && !event.metaKey + && !event.shiftKey) { + switch (event.keyCode) { + case 37: // left + var prevHref = $('link[rel="prev"]').prop('href'); + if (prevHref) { + window.location.href = prevHref; + return false; + } + break; + case 39: // right + var nextHref = $('link[rel="next"]').prop('href'); + if (nextHref) { + window.location.href = nextHref; + return false; + } + break; + } + } + }); + } +}; + +// quick alias for translations +_ = Documentation.gettext; + +$(document).ready(function() { + Documentation.init(); +}); diff --git a/v1.8.0/_static/documentation_options.js b/v1.8.0/_static/documentation_options.js new file mode 100644 index 00000000..75b5cf13 --- /dev/null +++ b/v1.8.0/_static/documentation_options.js @@ -0,0 +1,12 @@ +var DOCUMENTATION_OPTIONS = { + URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), + VERSION: '', + LANGUAGE: 'None', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: true +}; \ No newline at end of file diff --git a/v1.8.0/_static/favicon.png b/v1.8.0/_static/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..d03ecee2f9f6ee876437ff44169c60c77525ac94 GIT binary patch literal 1700 zcmV;V23z@wP)`i3Xp^XPAag24Hy$& z)?R#DZ0y~&&vp9Xu=d)!wo!TP?`{5<`F%4xXZ9Sy15#DpN9C5V&kG@YKy+nfj5RE) zIeGEK{y$!wt4};AfQr3mDnRX+9$T{Fw4GPn+C4s6^z@tiUp||vPkB%PGv#nj5G=W- zI9ytVCG3Wsf8O(M?Xg;QX5D@5z!R@LwqN_{iBXR z-Zy;%rPt%g z;^@y;u)502-`?+IUs;(Nx2&=b(^GavPW2R&UZr}?TL1)$+A#DCdOrI~^_Z4_qQ0Ij z_4U~E0&M;Q_^12Y@I2;tJZkU+r%`d)| zG?&vgPUW(5=$d!)r-OB;;P5t>;|y$g;b#x|e4QbO5?S1Ip^nJFDv*Ny5uNYsxpH>6tBQ0Ump4Z{Z?;^F@7J$+-N{9&{rwtx-CKyS|Jq zQ?E}b7MVGb*U|#$y7_*~rAITHFMSWw@E{!Sv_?*^hEVNU@#x(`S{ip@na-4bzF>Fx z6R+&AnRN!X{jRRQzcW}p9t$9)_?$TPBmhhIeu6X=lp7UrDmfQwUr)GmH7<7yDHT?I z?FfOw_Q^T1ab#%E&)5MeEF=1pU{N~`r-l%cOm75FW-KiLfN$_*B_k(+l5e_jXqC9U|1-Dp}N*DpU#f6vM}Vdm1v zP$jySiOU@az@2e7khB95xl`A?guB)dj|IptX`Ni5fUKMW)_n7?lvI6;qMB1pS!+Sf zT@Zkjg7H`|F}7`|Bk3kA-hSvBunZtrbUksA|(+p*DnPTA&bWvK> z2z&#Cio?H^a`QDX2Pb>7Bk*B9wmx(GruEh1J;(_pT3d&mW z<%Y0Q0E`TkF%kE({a1a7KNiE>%!IKiQ5@CX(y~=nZh6(~@kbPZvEc&i+}{r0`@s(bKUOQ@63L&le^Iml~cUbZh-wz6mJ~BVi8E)3K%}DW9}5 zRh(mn;?V#Addvc#uje7|3|21496&H--WRH2WZ(<`NDJT)j-(`jX?W1bbI_s|mg%Ie z>9GZy1NieMAI*u_B3c^1i$t7E3(y@H`==1fXEP^>j^<%%HqGaE5WQP)UjT-dMfc6G zbK&%l(I&E6J7c#$ObfuP&lroK;h{>TR0!Rux-WBp;lT>7Hr6s8%LS~19N%T7 z2LNuLc{CEq#v11ZfHkaBTN zrI)9_RY-3{r)AJfcD9%Dpoh|+2Va&-Xi($VZ*+EI%lgj48xDh*^Y>cthslcCW3CZt zY(KE0>fq`M*UCj%iYkPlyu2JwMixV(hS}B|zZr^XZ=5So3>RW&1_%311-}QgZ)_(!}4s5mUX1X!}0000s`hMrGg#P~ix$^RISR_I47Y|r1 z_CyJOe}D1){SET-^Amu_i71Lt6eYfZjRyw@I6OQAIXXHDfiX^GbOlHe=Ae4>0m)d(f|Me07*qoM6N<$f}vM^LjV8( literal 0 HcmV?d00001 diff --git a/v1.8.0/_static/images/logo_binder.svg b/v1.8.0/_static/images/logo_binder.svg new file mode 100644 index 00000000..45fecf75 --- /dev/null +++ b/v1.8.0/_static/images/logo_binder.svg @@ -0,0 +1,19 @@ + + + + +logo + + + + + + + + diff --git a/v1.8.0/_static/images/logo_colab.png b/v1.8.0/_static/images/logo_colab.png new file mode 100644 index 0000000000000000000000000000000000000000..b7560ec216b2d1b6f77855525fe966c741833428 GIT binary patch literal 7601 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 diff --git a/v1.8.0/_static/images/logo_jupyterhub.svg b/v1.8.0/_static/images/logo_jupyterhub.svg new file mode 100644 index 00000000..60cfe9f2 --- /dev/null +++ b/v1.8.0/_static/images/logo_jupyterhub.svg @@ -0,0 +1 @@ +logo_jupyterhubHub diff --git a/v1.8.0/_static/jquery-3.5.1.js b/v1.8.0/_static/jquery-3.5.1.js new file mode 100644 index 00000000..50937333 --- /dev/null +++ b/v1.8.0/_static/jquery-3.5.1.js @@ -0,0 +1,10872 @@ +/*! + * jQuery JavaScript Library v3.5.1 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2020-05-04T22:49Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var flat = arr.flat ? function( array ) { + return arr.flat.call( array ); +} : function( array ) { + return arr.concat.apply( [], array ); +}; + + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + +var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + return typeof obj === "function" && typeof obj.nodeType !== "number"; + }; + + +var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + +var document = window.document; + + + + var preservedScriptAttributes = { + type: true, + src: true, + nonce: true, + noModule: true + }; + + function DOMEval( code, node, doc ) { + doc = doc || document; + + var i, val, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + + // Support: Firefox 64+, Edge 18+ + // Some browsers don't support the "nonce" property on scripts. + // On the other hand, just using `getAttribute` is not enough as + // the `nonce` attribute is reset to an empty string whenever it + // becomes browsing-context connected. + // See https://github.com/whatwg/html/issues/2369 + // See https://html.spec.whatwg.org/#nonce-attributes + // The `node.getAttribute` check was added for the sake of + // `jQuery.globalEval` so that it can fake a nonce-containing node + // via an object. + val = node[ i ] || node.getAttribute && node.getAttribute( i ); + if ( val ) { + script.setAttribute( i, val ); + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.5.1", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + even: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return ( i + 1 ) % 2; + } ) ); + }, + + odd: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return i % 2; + } ) ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + copy = options[ name ]; + + // Prevent Object.prototype pollution + // Prevent never-ending loop + if ( name === "__proto__" || target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + src = target[ name ]; + + // Ensure proper type for the source value + if ( copyIsArray && !Array.isArray( src ) ) { + clone = []; + } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { + clone = {}; + } else { + clone = src; + } + copyIsArray = false; + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + // Evaluates a script in a provided context; falls back to the global one + // if not specified. + globalEval: function( code, options, doc ) { + DOMEval( code, { nonce: options && options.nonce }, doc ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return flat( ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( _i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); + + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.5 + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://js.foundation/ + * + * Date: 2020-03-14 + */ +( function( window ) { +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + nonnativeSelectorCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ( {} ).hasOwnProperty, + arr = [], + pop = arr.pop, + pushNative = arr.push, + push = arr.push, + slice = arr.slice, + + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[ i ] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" + + "ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram + identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + + "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + + // "Attribute values must be CSS identifiers [capture 5] + // or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + + whitespace + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + + "*" ), + rdescend = new RegExp( whitespace + "|>" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rhtml = /HTML$/i, + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ), + funescape = function( escape, nonHex ) { + var high = "0x" + escape.slice( 1 ) - 0x10000; + + return nonHex ? + + // Strip the backslash prefix from a non-hex escape sequence + nonHex : + + // Replace a hexadecimal escape sequence with the encoded Unicode code point + // Support: IE <=11+ + // For values outside the Basic Multilingual Plane (BMP), manually construct a + // surrogate pair + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + inDisabledFieldset = addCombinator( + function( elem ) { + return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + ( arr = slice.call( preferredDoc.childNodes ) ), + preferredDoc.childNodes + ); + + // Support: Android<4.0 + // Detect silently failing push.apply + // eslint-disable-next-line no-unused-expressions + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + pushNative.apply( target, slice.call( els ) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + + // Can't trust NodeList.length + while ( ( target[ j++ ] = els[ i++ ] ) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + setDocument( context ); + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) { + + // ID selector + if ( ( m = match[ 1 ] ) ) { + + // Document context + if ( nodeType === 9 ) { + if ( ( elem = context.getElementById( m ) ) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && ( elem = newContext.getElementById( m ) ) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[ 2 ] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( ( m = match[ 3 ] ) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !nonnativeSelectorCache[ selector + " " ] && + ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) && + + // Support: IE 8 only + // Exclude object elements + ( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) { + + newSelector = selector; + newContext = context; + + // qSA considers elements outside a scoping root when evaluating child or + // descendant combinators, which is not what we want. + // In such cases, we work around the behavior by prefixing every selector in the + // list with an ID selector referencing the scope context. + // The technique has to be used as well when a leading combinator is used + // as such selectors are not recognized by querySelectorAll. + // Thanks to Andrew Dupont for this technique. + if ( nodeType === 1 && + ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) { + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + + // We can use :scope instead of the ID hack if the browser + // supports it & if we're not changing the context. + if ( newContext !== context || !support.scope ) { + + // Capture the context ID, setting it first if necessary + if ( ( nid = context.getAttribute( "id" ) ) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", ( nid = expando ) ); + } + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " + + toSelector( groups[ i ] ); + } + newSelector = groups.join( "," ); + } + + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + nonnativeSelectorCache( selector, true ); + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return ( cache[ key + " " ] = value ); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement( "fieldset" ); + + try { + return !!fn( el ); + } catch ( e ) { + return false; + } finally { + + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split( "|" ), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[ i ] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( ( cur = cur.nextSibling ) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return ( name === "input" || name === "button" ) && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + inDisabledFieldset( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction( function( argument ) { + argument = +argument; + return markFunction( function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ ( j = matchIndexes[ i ] ) ] ) { + seed[ j ] = !( matches[ j ] = seed[ j ] ); + } + } + } ); + } ); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + var namespace = elem.namespaceURI, + docElem = ( elem.ownerDocument || elem ).documentElement; + + // Support: IE <=8 + // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes + // https://bugs.jquery.com/ticket/4833 + return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9 - 11+, Edge 12 - 18+ + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( preferredDoc != document && + ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } + + // Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only, + // Safari 4 - 5 only, Opera <=11.6 - 12.x only + // IE/Edge & older browsers don't support the :scope pseudo-class. + // Support: Safari 6.0 only + // Safari 6.0 supports :scope but it's an alias of :root there. + support.scope = assert( function( el ) { + docElem.appendChild( el ).appendChild( document.createElement( "div" ) ); + return typeof el.querySelectorAll !== "undefined" && + !el.querySelectorAll( ":scope fieldset div" ).length; + } ); + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert( function( el ) { + el.className = "i"; + return !el.getAttribute( "className" ); + } ); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert( function( el ) { + el.appendChild( document.createComment( "" ) ); + return !el.getElementsByTagName( "*" ).length; + } ); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert( function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + } ); + + // ID filter and find + if ( support.getById ) { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute( "id" ) === attrId; + }; + }; + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode( "id" ); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( ( elem = elems[ i++ ] ) ) { + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find[ "TAG" ] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) { + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert( function( el ) { + + var input; + + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll( "[selected]" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push( "~=" ); + } + + // Support: IE 11+, Edge 15 - 18+ + // IE 11/Edge don't find elements on a `[name='']` query in some cases. + // Adding a temporary attribute to the document before the selection works + // around the issue. + // Interestingly, IE 10 & older don't seem to have the issue. + input = document.createElement( "input" ); + input.setAttribute( "name", "" ); + el.appendChild( input ); + if ( !el.querySelectorAll( "[name='']" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + + whitespace + "*(?:''|\"\")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll( ":checked" ).length ) { + rbuggyQSA.push( ":checked" ); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push( ".#.+[+~]" ); + } + + // Support: Firefox <=3.6 - 5 only + // Old Firefox doesn't throw on a badly-escaped identifier. + el.querySelectorAll( "\\\f" ); + rbuggyQSA.push( "[\\r\\n\\f]" ); + } ); + + assert( function( el ) { + el.innerHTML = "" + + ""; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement( "input" ); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll( "[name=d]" ).length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll( ":enabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: Opera 10 - 11 only + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll( "*,:x" ); + rbuggyQSA.push( ",.*:" ); + } ); + } + + if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector ) ) ) ) { + + assert( function( el ) { + + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + } ); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + ) ); + } : + function( a, b ) { + if ( b ) { + while ( ( b = b.parentNode ) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + ( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) { + + // Choose the first element that is related to our preferred document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( a == document || a.ownerDocument == preferredDoc && + contains( preferredDoc, a ) ) { + return -1; + } + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( b == document || b.ownerDocument == preferredDoc && + contains( preferredDoc, b ) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + return a == document ? -1 : + b == document ? 1 : + /* eslint-enable eqeqeq */ + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( ( cur = cur.parentNode ) ) { + ap.unshift( cur ); + } + cur = b; + while ( ( cur = cur.parentNode ) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[ i ] === bp[ i ] ) { + i++; + } + + return i ? + + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[ i ], bp[ i ] ) : + + // Otherwise nodes in our document sort first + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + ap[ i ] == preferredDoc ? -1 : + bp[ i ] == preferredDoc ? 1 : + /* eslint-enable eqeqeq */ + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + setDocument( elem ); + + if ( support.matchesSelector && documentIsHTML && + !nonnativeSelectorCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch ( e ) { + nonnativeSelectorCache( expr, true ); + } + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( context.ownerDocument || context ) != document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( elem.ownerDocument || elem ) != document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; +}; + +Sizzle.escape = function( sel ) { + return ( sel + "" ).replace( rcssescape, fcssescape ); +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + + // If no nodeType, this is expected to be an array + while ( ( node = elem[ i++ ] ) ) { + + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[ 1 ] = match[ 1 ].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[ 3 ] = ( match[ 3 ] || match[ 4 ] || + match[ 5 ] || "" ).replace( runescape, funescape ); + + if ( match[ 2 ] === "~=" ) { + match[ 3 ] = " " + match[ 3 ] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[ 1 ] = match[ 1 ].toLowerCase(); + + if ( match[ 1 ].slice( 0, 3 ) === "nth" ) { + + // nth-* requires argument + if ( !match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[ 4 ] = +( match[ 4 ] ? + match[ 5 ] + ( match[ 6 ] || 1 ) : + 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) ); + match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" ); + + // other types prohibit arguments + } else if ( match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[ 6 ] && match[ 2 ]; + + if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[ 3 ] ) { + match[ 2 ] = match[ 4 ] || match[ 5 ] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + + // Get excess from tokenize (recursively) + ( excess = tokenize( unquoted, true ) ) && + + // advance to the next closing parenthesis + ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) { + + // excess is a negative index + match[ 0 ] = match[ 0 ].slice( 0, excess ); + match[ 2 ] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { + return true; + } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + ( pattern = new RegExp( "(^|" + whitespace + + ")" + className + "(" + whitespace + "|$)" ) ) && classCache( + className, function( elem ) { + return pattern.test( + typeof elem.className === "string" && elem.className || + typeof elem.getAttribute !== "undefined" && + elem.getAttribute( "class" ) || + "" + ); + } ); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + /* eslint-disable max-len */ + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + /* eslint-enable max-len */ + + }; + }, + + "CHILD": function( type, what, _argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, _context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( ( node = node[ dir ] ) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( ( node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + + // Use previously-cached element index if available + if ( useCache ) { + + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + + // Use the same loop as above to seek `elem` from the start + while ( ( node = ++nodeIndex && node && node[ dir ] || + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || + ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction( function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[ i ] ); + seed[ idx ] = !( matches[ idx ] = matched[ i ] ); + } + } ) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + + // Potentially complex pseudos + "not": markFunction( function( selector ) { + + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction( function( seed, matches, _context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( ( elem = unmatched[ i ] ) ) { + seed[ i ] = !( matches[ i ] = elem ); + } + } + } ) : + function( elem, _context, xml ) { + input[ 0 ] = elem; + matcher( input, null, xml, results ); + + // Don't keep the element (issue #299) + input[ 0 ] = null; + return !results.pop(); + }; + } ), + + "has": markFunction( function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + } ), + + "contains": markFunction( function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; + }; + } ), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + + // lang value must be a valid identifier + if ( !ridentifier.test( lang || "" ) ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( ( elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 ); + return false; + }; + } ), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && + ( !document.hasFocus || document.hasFocus() ) && + !!( elem.type || elem.href || ~elem.tabIndex ); + }, + + // Boolean properties + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), + + "checked": function( elem ) { + + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return ( nodeName === "input" && !!elem.checked ) || + ( nodeName === "option" && !!elem.selected ); + }, + + "selected": function( elem ) { + + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + // eslint-disable-next-line no-unused-expressions + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos[ "empty" ]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( ( attr = elem.getAttribute( "type" ) ) == null || + attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo( function() { + return [ 0 ]; + } ), + + "last": createPositionalPseudo( function( _matchIndexes, length ) { + return [ length - 1 ]; + } ), + + "eq": createPositionalPseudo( function( _matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + } ), + + "even": createPositionalPseudo( function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "odd": createPositionalPseudo( function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "lt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? + argument + length : + argument > length ? + length : + argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "gt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ) + } +}; + +Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || ( match = rcomma.exec( soFar ) ) ) { + if ( match ) { + + // Don't consume trailing commas as valid + soFar = soFar.slice( match[ 0 ].length ) || soFar; + } + groups.push( ( tokens = [] ) ); + } + + matched = false; + + // Combinators + if ( ( match = rcombinators.exec( soFar ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + + // Cast descendant combinators to space + type: match[ 0 ].replace( rtrim, " " ) + } ); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] || + ( match = preFilters[ type ]( match ) ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + type: type, + matches: match + } ); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[ i ].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; + + return combinator.first ? + + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || ( elem[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || + ( outerCache[ elem.uniqueID ] = {} ); + + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( ( oldCache = uniqueCache[ key ] ) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return ( newCache[ 2 ] = oldCache[ 2 ] ); + } else { + + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) { + return true; + } + } + } + } + } + return false; + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[ i ]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[ 0 ]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[ i ], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( ( elem = unmatched[ i ] ) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction( function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( + selector || "*", + context.nodeType ? [ context ] : context, + [] + ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( ( elem = temp[ i ] ) ) { + matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem ); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) ) { + + // Restore matcherIn since elem is not yet a final match + temp.push( ( matcherIn[ i ] = elem ) ); + } + } + postFinder( null, ( matcherOut = [] ), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) && + ( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) { + + seed[ temp ] = !( results[ temp ] = elem ); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + } ); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[ 0 ].type ], + implicitRelative = leadingRelative || Expr.relative[ " " ], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + ( checkContext = context ).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) { + matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; + } else { + matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[ j ].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens + .slice( 0, i - 1 ) + .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } ) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ), + + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ), + len = elems.length; + + if ( outermost ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + outermostContext = context == document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( !context && elem.ownerDocument != document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( ( matcher = elementMatchers[ j++ ] ) ) { + if ( matcher( elem, context || document, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + + // They will have gone through all possible matchers + if ( ( elem = !matcher && elem ) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( ( matcher = setMatchers[ j++ ] ) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !( unmatched[ i ] || setMatched[ i ] ) ) { + setMatched[ i ] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[ i ] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( + selector, + matcherFromGroupMatchers( elementMatchers, setMatchers ) + ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( ( selector = compiled.selector || selector ) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[ 0 ] = match[ 0 ].slice( 0 ); + if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" && + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) { + + context = ( Expr.find[ "ID" ]( token.matches[ 0 ] + .replace( runescape, funescape ), context ) || [] )[ 0 ]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[ i ]; + + // Abort if we hit a combinator + if ( Expr.relative[ ( type = token.type ) ] ) { + break; + } + if ( ( find = Expr.find[ type ] ) ) { + + // Search, expanding context for leading sibling combinators + if ( ( seed = find( + token.matches[ 0 ].replace( runescape, funescape ), + rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) || + context + ) ) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert( function( el ) { + + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1; +} ); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert( function( el ) { + el.innerHTML = ""; + return el.firstChild.getAttribute( "href" ) === "#"; +} ) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + } ); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert( function( el ) { + el.innerHTML = ""; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; +} ) ) { + addHandle( "value", function( elem, _name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + } ); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert( function( el ) { + return el.getAttribute( "disabled" ) == null; +} ) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; + } + } ); +} + +return Sizzle; + +} )( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; + +// Deprecated +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + + + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +}; +var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + + + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } + + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } + + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } + + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); + + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, _i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, _i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, _i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( elem.contentDocument != null && + + // Support: IE 11+ + // elements with no `data` attribute has an object + // `contentDocument` with a `null` prototype. + getProto( elem.contentDocument ) ) { + + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); +var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} + +function adoptValue( value, resolve, reject, noValue ) { + var method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( _i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the master Deferred + master = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + master.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( master.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return master.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); + } + + return master.promise(); + } +} ); + + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + + + + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; + + + + +// The deferred used on DOM ready +var readyList = jQuery.Deferred(); + +jQuery.fn.ready = function( fn ) { + + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } +} ); + +jQuery.ready.then = readyList.then; + +// The ready event handler and self cleanup method +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + +} else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, _key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; +}; + + +// Matches dashed string for camelizing +var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; + +// Used by camelCase as callback to replace() +function fcamelCase( _all, letter ) { + return letter.toUpperCase(); +} + +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) +function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); +} +var acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + + + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + +function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; +} + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var documentElement = document.documentElement; + + + + var isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ); + }, + composed = { composed: true }; + + // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only + // Check attachment across shadow DOM boundaries when possible (gh-3504) + // Support: iOS 10.0-10.2 only + // Early iOS 10 versions support `attachShadow` but not `getRootNode`, + // leading to errors. We need to check for `getRootNode`. + if ( documentElement.getRootNode ) { + isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ) || + elem.getRootNode( composed ) === elem.ownerDocument; + }; + } +var isHiddenWithinTree = function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + isAttached( elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = elem.nodeType && + ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + while ( maxIterations-- ) { + + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; + + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +var defaultDisplayMap = {}; + +function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; +} + +function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; +} + +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); + +var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); + + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + + // Support: IE <=9 only + // IE <=9 replaces "; + support.option = !!div.lastChild; +} )(); + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [ 1, "", "
" ], + col: [ 2, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + _default: [ 0, "", "" ] +}; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// Support: IE <=9 only +if ( !support.option ) { + wrapMap.optgroup = wrapMap.option = [ 1, "" ]; +} + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, attached, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + attached = isAttached( elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( attached ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 - 11+ +// focus() and blur() are asynchronous, except when they are no-op. +// So expect focus to be synchronous when the element is already active, +// and blur to be synchronous when the element is not already active. +// (focus and blur are always synchronous in other supported browsers, +// this just defines when we can count on it). +function expectSync( elem, type ) { + return ( elem === safeActiveElement() ) === ( type === "focus" ); +} + +// Support: IE <=9 only +// Accessing document.activeElement can throw unexpectedly +// https://bugs.jquery.com/ticket/13393 +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Only attach events to objects that accept data + if ( !acceptData( elem ) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = Object.create( null ); + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( nativeEvent ), + + handlers = ( + dataPriv.get( this, "events" ) || Object.create( null ) + )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // If the event is namespaced, then each handler is only invoked if it is + // specially universal or its namespaces are a superset of the event's. + if ( !event.rnamespace || handleObj.namespace === false || + event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + click: { + + // Utilize native event to ensure correct state for checkable inputs + setup: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Claim the first handler + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + // dataPriv.set( el, "click", ... ) + leverageNative( el, "click", returnTrue ); + } + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Force setup before triggering a click + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + leverageNative( el, "click" ); + } + + // Return non-false to allow normal event-path propagation + return true; + }, + + // For cross-browser consistency, suppress native .click() on links + // Also prevent it if we're currently inside a leveraged native-event stack + _default: function( event ) { + var target = event.target; + return rcheckableType.test( target.type ) && + target.click && nodeName( target, "input" ) && + dataPriv.get( target, "click" ) || + nodeName( target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +// Ensure the presence of an event listener that handles manually-triggered +// synthetic events by interrupting progress until reinvoked in response to +// *native* events that it fires directly, ensuring that state changes have +// already occurred before other listeners are invoked. +function leverageNative( el, type, expectSync ) { + + // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add + if ( !expectSync ) { + if ( dataPriv.get( el, type ) === undefined ) { + jQuery.event.add( el, type, returnTrue ); + } + return; + } + + // Register the controller as a special universal handler for all event namespaces + dataPriv.set( el, type, false ); + jQuery.event.add( el, type, { + namespace: false, + handler: function( event ) { + var notAsync, result, + saved = dataPriv.get( this, type ); + + if ( ( event.isTrigger & 1 ) && this[ type ] ) { + + // Interrupt processing of the outer synthetic .trigger()ed event + // Saved data should be false in such cases, but might be a leftover capture object + // from an async native handler (gh-4350) + if ( !saved.length ) { + + // Store arguments for use when handling the inner native event + // There will always be at least one argument (an event object), so this array + // will not be confused with a leftover capture object. + saved = slice.call( arguments ); + dataPriv.set( this, type, saved ); + + // Trigger the native event and capture its result + // Support: IE <=9 - 11+ + // focus() and blur() are asynchronous + notAsync = expectSync( this, type ); + this[ type ](); + result = dataPriv.get( this, type ); + if ( saved !== result || notAsync ) { + dataPriv.set( this, type, false ); + } else { + result = {}; + } + if ( saved !== result ) { + + // Cancel the outer synthetic event + event.stopImmediatePropagation(); + event.preventDefault(); + return result.value; + } + + // If this is an inner synthetic event for an event with a bubbling surrogate + // (focus or blur), assume that the surrogate already propagated from triggering the + // native event and prevent that from happening again here. + // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the + // bubbling surrogate propagates *after* the non-bubbling base), but that seems + // less bad than duplication. + } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { + event.stopPropagation(); + } + + // If this is a native event triggered above, everything is now in order + // Fire an inner synthetic event with the original arguments + } else if ( saved.length ) { + + // ...and capture the result + dataPriv.set( this, type, { + value: jQuery.event.trigger( + + // Support: IE <=9 - 11+ + // Extend with the prototype to reset the above stopImmediatePropagation() + jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), + saved.slice( 1 ), + this + ) + } ); + + // Abort handling of the native event + event.stopImmediatePropagation(); + } + } + } ); +} + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + code: true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + + which: function( event ) { + var button = event.button; + + // Add which for key events + if ( event.which == null && rkeyEvent.test( event.type ) ) { + return event.charCode != null ? event.charCode : event.keyCode; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { + if ( button & 1 ) { + return 1; + } + + if ( button & 2 ) { + return 3; + } + + if ( button & 4 ) { + return 2; + } + + return 0; + } + + return event.which; + } +}, jQuery.event.addProp ); + +jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { + jQuery.event.special[ type ] = { + + // Utilize native event if possible so blur/focus sequence is correct + setup: function() { + + // Claim the first handler + // dataPriv.set( this, "focus", ... ) + // dataPriv.set( this, "blur", ... ) + leverageNative( this, type, expectSync ); + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function() { + + // Force setup before trigger + leverageNative( this, type ); + + // Return non-false to allow normal event-path propagation + return true; + }, + + delegateType: delegateType + }; +} ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +var + + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g; + +// Prefer a tbody over its parent table for containing new rows +function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.get( src ); + events = pdataOld.events; + + if ( events ) { + dataPriv.remove( dest, "handle events" ); + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = flat( args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + valueIsFunction = isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl && !node.noModule ) { + jQuery._evalUrl( node.src, { + nonce: node.nonce || node.getAttribute( "nonce" ) + }, doc ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && isAttached( node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html; + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = isAttached( elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); +var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +var getStyles = function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + var view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; + +var swap = function( elem, options, callback ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.call( elem ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + +var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); + + + +( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + container.style.cssText = "position:absolute;left:-11111px;width:60px;" + + "margin-top:1px;padding:0;border:0"; + div.style.cssText = + "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + + "margin:auto;border:1px;padding:1px;" + + "width:60%;top:1%"; + documentElement.appendChild( container ).appendChild( div ); + + var divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; + + // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 + // Some styles come back with percentage values, even though they shouldn't + div.style.right = "60%"; + pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; + + // Support: IE 9 - 11 only + // Detect misreporting of content dimensions for box-sizing:border-box elements + boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; + + // Support: IE 9 only + // Detect overflow:scroll screwiness (gh-3699) + // Support: Chrome <=64 + // Don't get tricked when zoom affects offsetWidth (gh-4029) + div.style.position = "absolute"; + scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + function roundPixelMeasures( measure ) { + return Math.round( parseFloat( measure ) ); + } + + var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, + reliableTrDimensionsVal, reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + jQuery.extend( support, { + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelBoxStyles: function() { + computeStyleTests(); + return pixelBoxStylesVal; + }, + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + }, + scrollboxSize: function() { + computeStyleTests(); + return scrollboxSizeVal; + }, + + // Support: IE 9 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Behavior in IE 9 is more subtle than in newer versions & it passes + // some versions of this test; make sure not to make it pass there! + reliableTrDimensions: function() { + var table, tr, trChild, trStyle; + if ( reliableTrDimensionsVal == null ) { + table = document.createElement( "table" ); + tr = document.createElement( "tr" ); + trChild = document.createElement( "div" ); + + table.style.cssText = "position:absolute;left:-11111px"; + tr.style.height = "1px"; + trChild.style.height = "9px"; + + documentElement + .appendChild( table ) + .appendChild( tr ) + .appendChild( trChild ); + + trStyle = window.getComputedStyle( tr ); + reliableTrDimensionsVal = parseInt( trStyle.height ) > 3; + + documentElement.removeChild( table ); + } + return reliableTrDimensionsVal; + } + } ); +} )(); + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + + computed = computed || getStyles( elem ); + + // getPropertyValue is needed for: + // .css('filter') (IE 9 only, #12537) + // .css('--customProperty) (#3144) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !isAttached( elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + + +function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; +} + + +var cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style, + vendorProps = {}; + +// Return a vendor-prefixed property or undefined +function vendorPropName( name ) { + + // Check for vendor prefixed names + var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } +} + +// Return a potentially-mapped jQuery.cssProps or vendor prefixed property +function finalPropName( name ) { + var final = jQuery.cssProps[ name ] || vendorProps[ name ]; + + if ( final ) { + return final; + } + if ( name in emptyStyle ) { + return name; + } + return vendorProps[ name ] = vendorPropName( name ) || name; +} + + +var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }; + +function setPositiveNumber( _elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; +} + +function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { + var i = dimension === "width" ? 1 : 0, + extra = 0, + delta = 0; + + // Adjustment may not be necessary + if ( box === ( isBorderBox ? "border" : "content" ) ) { + return 0; + } + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin + if ( box === "margin" ) { + delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + } + + // If we get here with a content-box, we're seeking "padding" or "border" or "margin" + if ( !isBorderBox ) { + + // Add padding + delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // For "border" or "margin", add border + if ( box !== "padding" ) { + delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + + // But still keep track of it otherwise + } else { + extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + + // If we get here with a border-box (content + padding + border), we're seeking "content" or + // "padding" or "margin" + } else { + + // For "content", subtract padding + if ( box === "content" ) { + delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // For "content" or "padding", subtract border + if ( box !== "margin" ) { + delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + // Account for positive content-box scroll gutter when requested by providing computedVal + if ( !isBorderBox && computedVal >= 0 ) { + + // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border + // Assuming integer scroll gutter, subtract the rest and round down + delta += Math.max( 0, Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + computedVal - + delta - + extra - + 0.5 + + // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter + // Use an explicit zero to avoid NaN (gh-3964) + ) ) || 0; + } + + return delta; +} + +function getWidthOrHeight( elem, dimension, extra ) { + + // Start with computed style + var styles = getStyles( elem ), + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). + // Fake content-box until we know it's needed to know the true value. + boxSizingNeeded = !support.boxSizingReliable() || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + valueIsBorderBox = isBorderBox, + + val = curCSS( elem, dimension, styles ), + offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); + + // Support: Firefox <=54 + // Return a confounding non-pixel value or feign ignorance, as appropriate. + if ( rnumnonpx.test( val ) ) { + if ( !extra ) { + return val; + } + val = "auto"; + } + + + // Support: IE 9 - 11 only + // Use offsetWidth/offsetHeight for when box sizing is unreliable. + // In those cases, the computed value can be trusted to be border-box. + if ( ( !support.boxSizingReliable() && isBorderBox || + + // Support: IE 10 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Interestingly, in some cases IE 9 doesn't suffer from this issue. + !support.reliableTrDimensions() && nodeName( elem, "tr" ) || + + // Fall back to offsetWidth/offsetHeight when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + val === "auto" || + + // Support: Android <=4.1 - 4.3 only + // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) + !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && + + // Make sure the element is visible & connected + elem.getClientRects().length ) { + + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // Where available, offsetWidth/offsetHeight approximate border box dimensions. + // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the + // retrieved value as a content box dimension. + valueIsBorderBox = offsetProp in elem; + if ( valueIsBorderBox ) { + val = elem[ offsetProp ]; + } + } + + // Normalize "" and auto + val = parseFloat( val ) || 0; + + // Adjust for the element's box model + return ( val + + boxModelAdjustment( + elem, + dimension, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles, + + // Provide the current computed size to request scroll gutter calculation (gh-3589) + val + ) + ) + "px"; +} + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "gridArea": true, + "gridColumn": true, + "gridColumnEnd": true, + "gridColumnStart": true, + "gridRow": true, + "gridRowEnd": true, + "gridRowStart": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: {}, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ), + style = elem.style; + + // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append + // "px" to a few hardcoded values. + if ( type === "number" && !isCustomProp ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + if ( isCustomProp ) { + style.setProperty( name, value ); + } else { + style[ name ] = value; + } + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ); + + // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + + return val; + } +} ); + +jQuery.each( [ "height", "width" ], function( _i, dimension ) { + jQuery.cssHooks[ dimension ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, dimension, extra ); + } ) : + getWidthOrHeight( elem, dimension, extra ); + } + }, + + set: function( elem, value, extra ) { + var matches, + styles = getStyles( elem ), + + // Only read styles.position if the test has a chance to fail + // to avoid forcing a reflow. + scrollboxSizeBuggy = !support.scrollboxSize() && + styles.position === "absolute", + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) + boxSizingNeeded = scrollboxSizeBuggy || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + subtract = extra ? + boxModelAdjustment( + elem, + dimension, + extra, + isBorderBox, + styles + ) : + 0; + + // Account for unreliable border-box dimensions by comparing offset* to computed and + // faking a content-box to get border and padding (gh-3699) + if ( isBorderBox && scrollboxSizeBuggy ) { + subtract -= Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + parseFloat( styles[ dimension ] ) - + boxModelAdjustment( elem, dimension, "border", false, styles ) - + 0.5 + ); + } + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ dimension ] = value; + value = jQuery.css( elem, dimension ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; +} ); + +jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each( { + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( prefix !== "margin" ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +} ); + +jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( Array.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } +} ); + + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && ( + jQuery.cssHooks[ tween.prop ] || + tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" +}; + +jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, inProgress, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + +function schedule() { + if ( inProgress ) { + if ( document.hidden === false && window.requestAnimationFrame ) { + window.requestAnimationFrame( schedule ); + } else { + window.setTimeout( schedule, jQuery.fx.interval ); + } + + jQuery.fx.tick(); + } +} + +// Animations created synchronously will run synchronously +function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = Date.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + var tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 15 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY and Edge just mirrors + // the overflowX value there. + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( Array.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + // If there's more to do, yield + if ( percent < 1 && length ) { + return remaining; + } + + // If this was an empty animation, synthesize a final progress notification + if ( !length ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + } + + // Resolve the animation and report its conclusion + deferred.resolveWith( elem, [ animation ] ); + return false; + }, + animation = deferred.promise( { + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + result.stop.bind( result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + // Attach callbacks from options + animation + .progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + return animation; +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnothtmlwhite ); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } +} ); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !isFunction( easing ) && easing + }; + + // Go to the end state if fx are off + if ( jQuery.fx.off ) { + opt.duration = 0; + + } else { + if ( typeof opt.duration !== "number" ) { + if ( opt.duration in jQuery.fx.speeds ) { + opt.duration = jQuery.fx.speeds[ opt.duration ]; + + } else { + opt.duration = jQuery.fx.speeds._default; + } + } + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + var index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } +} ); + +jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +} ); + +// Generate shortcuts for custom animations +jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +} ); + +jQuery.timers = []; +jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = Date.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Run the timer and safely remove it when done (allowing for external removal) + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + jQuery.fx.start(); +}; + +jQuery.fx.interval = 13; +jQuery.fx.start = function() { + if ( inProgress ) { + return; + } + + inProgress = true; + schedule(); +}; + +jQuery.fx.stop = function() { + inProgress = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); +}; + + +( function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +} )(); + + +var boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, + i = 0, + + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match( rnothtmlwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; +} ); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + if ( tabindex ) { + return parseInt( tabindex, 10 ); + } + + if ( + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && + elem.href + ) { + return 0; + } + + return -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +// eslint rule "no-unused-expressions" is disabled for this code +// since it considers such accessions noop +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + + + + + // Strip and collapse whitespace according to HTML spec + // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace + function stripAndCollapse( value ) { + var tokens = value.match( rnothtmlwhite ) || []; + return tokens.join( " " ); + } + + +function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; +} + +function classesToArray( value ) { + if ( Array.isArray( value ) ) { + return value; + } + if ( typeof value === "string" ) { + return value.match( rnothtmlwhite ) || []; + } + return []; +} + +jQuery.fn.extend( { + addClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isValidValue = type === "string" || Array.isArray( value ); + + if ( typeof stateVal === "boolean" && isValidValue ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( isValidValue ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = classesToArray( value ); + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { + return true; + } + } + + return false; + } +} ); + + + + +var rreturn = /\r/g; + +jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, valueIsFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + // Handle most common string cases + if ( typeof ret === "string" ) { + return ret.replace( rreturn, "" ); + } + + // Handle cases where value is null/undef or number + return ret == null ? "" : ret; + } + + return; + } + + valueIsFunction = isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( valueIsFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( Array.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if ( index < 0 ) { + i = max; + + } else { + i = one ? index : 0; + } + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( Array.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + + + + +// Return jQuery for attributes-only inclusion + + +support.focusin = "onfocusin" in window; + + +var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + stopPropagationCallback = function( e ) { + e.stopPropagation(); + }; + +jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = lastElement = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + lastElement = cur; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( + dataPriv.get( cur, "events" ) || Object.create( null ) + )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + + if ( event.isPropagationStopped() ) { + lastElement.addEventListener( type, stopPropagationCallback ); + } + + elem[ type ](); + + if ( event.isPropagationStopped() ) { + lastElement.removeEventListener( type, stopPropagationCallback ); + } + + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + +} ); + +jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +// Support: Firefox <=44 +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + + // Handle: regular nodes (via `this.ownerDocument`), window + // (via `this.document`) & document (via `this`). + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + dataPriv.remove( doc, fix ); + + } else { + dataPriv.access( doc, fix, attaches ); + } + } + }; + } ); +} +var location = window.location; + +var nonce = { guid: Date.now() }; + +var rquery = ( /\?/ ); + + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) { + xml = undefined; + } + + if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; +}; + + +var + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( Array.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && toType( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + var value = isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + if ( a == null ) { + return ""; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); +}; + +jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ) + .filter( function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ) + .map( function( _i, elem ) { + var val = jQuery( this ).val(); + + if ( val == null ) { + return null; + } + + if ( Array.isArray( val ) ) { + return jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ); + } + + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } +} ); + + +var + r20 = /%20/g, + rhash = /#.*$/, + rantiCache = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; + + if ( isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() + " " ] = + ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) + .concat( match[ 2 ] ); + } + } + match = responseHeaders[ key.toLowerCase() + " " ]; + } + return match == null ? null : match.join( ", " ); + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 15 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available and should be processed, append data to url + if ( s.data && ( s.processData || typeof s.data === "string" ) ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add or update anti-cache param if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rantiCache, "$1" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) + + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Use a noop converter for missing script + if ( !isSuccess && jQuery.inArray( "script", s.dataTypes ) > -1 ) { + s.converters[ "text script" ] = function() {}; + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +} ); + +jQuery.each( [ "get", "post" ], function( _i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); + +jQuery.ajaxPrefilter( function( s ) { + var i; + for ( i in s.headers ) { + if ( i.toLowerCase() === "content-type" ) { + s.contentType = s.headers[ i ] || ""; + } + } +} ); + + +jQuery._evalUrl = function( url, options, doc ) { + return jQuery.ajax( { + url: url, + + // Make this explicit, since user can override this through ajaxSetup (#11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + + // Only evaluate the response if it is successful (gh-4126) + // dataFilter is not invoked for failure responses, so using it instead + // of the default converter is kludgy but it works. + converters: { + "text script": function() {} + }, + dataFilter: function( response ) { + jQuery.globalEval( response, options, doc ); + } + } ); +}; + + +jQuery.fn.extend( { + wrapAll: function( html ) { + var wrap; + + if ( this[ 0 ] ) { + if ( isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var htmlIsFunction = isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } +} ); + + +jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); +}; +jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); +}; + + + + +jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} +}; + +var xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport( function( options ) { + var callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.ontimeout = + xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + +// Install script dataType +jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +} ); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +} ); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain or forced-by-attrs requests + if ( s.crossDomain || s.scriptAttrs ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery( " +{% endmacro %} \ No newline at end of file diff --git a/v1.8.0/about.html b/v1.8.0/about.html new file mode 100644 index 00000000..fca20857 --- /dev/null +++ b/v1.8.0/about.html @@ -0,0 +1,581 @@ + + + + + + + + + + Why use Pooch? | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+ Contents +
+ +
+
+
+
+
+ +
+

Why use Pooch?

+ +
+
+ +
+

Contents

+
+ +
+
+
+ +
+ +
+

Why use Pooch?

+
+

Use cases

+
+ +
+

Who: Scientists/researchers/developers looking to simply download a +file.

+

Pooch makes it easy to download a file (one function call). +On top of that, it also comes with some bonus features:

+
    +
  • Download and cache your data files locally (so it’s only downloaded +once).

  • +
  • Make sure everyone running the code has the same version of the data +files by verifying cryptographic hashes.

  • +
  • Multiple download protocols HTTP/FTP/SFTP and basic authentication.

  • +
  • Download from Digital Object Identifiers (DOIs) issued by repositories +like figshare and Zenodo.

  • +
  • Built-in utilities to unzip/decompress files upon download

  • +
+

Start here: Retrieving a single data file

+
+ +
+

Who: Package developers wanting to include sample data for use in +tutorials and tests.

+

Pooch was designed for this! It offers:

+
    +
  • Pure Python and minimal dependencies.

  • +
  • Download a file only if necessary.

  • +
  • Verification of download integrity through cryptographic hashes.

  • +
  • Extensible design: plug in custom download and post-processing functions.

  • +
  • Built-in utilities to unzip/decompress files upon download

  • +
  • Multiple download protocols HTTP/FTP/SFTP and basic authentication.

  • +
  • User control of data cache location through environment variables.

  • +
+

Start here: Manage a package’s sample data

+
+
+
+
+

History

+

Pooch was born out of shared need between the +Fatiando a Terra libraries and +MetPy. +During the +Scipy Conference 2018 +sprints, developers from both projects got together and, realizing the shared +necessity, devised a package that would combine the best of the existing +functionality already present in each project and extend it’s capabilities.

+
+
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/api/generated/pooch.DOIDownloader.html b/v1.8.0/api/generated/pooch.DOIDownloader.html new file mode 100644 index 00000000..72f69b98 --- /dev/null +++ b/v1.8.0/api/generated/pooch.DOIDownloader.html @@ -0,0 +1,599 @@ + + + + + + + + + + pooch.DOIDownloader | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

pooch.DOIDownloader

+ +
+
+ +
+
+
+ +
+ +
+

pooch.DOIDownloader

+
+
+class pooch.DOIDownloader(progressbar=False, chunk_size=1024, **kwargs)[source]
+

Download manager for fetching files from Digital Object Identifiers (DOIs).

+

Open-access data repositories often issue Digital Object Identifiers (DOIs) +for data which provide a stable link and citation point. The trick is +finding out the download URL for a file given the DOI.

+

When called, this downloader uses the repository’s public API to find out +the download URL from the DOI and file name. It then uses +pooch.HTTPDownloader to download the URL into the specified local +file. Allowing “URL”s to be specified with the DOI instead of the actual +HTTP download link. Uses the requests library to manage downloads +and interact with the APIs.

+

The format of the “URL” is: doi:{DOI}/{file name}.

+

Notice that there are no // like in HTTP/FTP and you must specify a +file name after the DOI (separated by a /).

+

Use with pooch.Pooch.fetch or pooch.retrieve to be able to +download files given the DOI instead of an HTTP link.

+

Supported repositories:

+ +
+

Attention

+

DOIs from other repositories will not work since we need to access +their particular APIs to find the download links. We welcome +suggestions and contributions adding new repositories.

+
+
+
Parameters
+
    +
  • progressbar (bool or an arbitrary progress bar object) – If True, will print a progress bar of the download to standard error +(stderr). Requires tqdm to be +installed. Alternatively, an arbitrary progress bar object can be +passed. See Using custom progress bars for details.

  • +
  • chunk_size (int) – Files are streamed chunk_size bytes at a time instead of loading +everything into memory at one. Usually doesn’t need to be changed.

  • +
  • **kwargs – All keyword arguments given when creating an instance of this class +will be passed to requests.get.

  • +
+
+
+

Examples

+

Download one of the data files from the figshare archive of Pooch test +data:

+
>>> import os
+>>> downloader = DOIDownloader()
+>>> url = "doi:10.6084/m9.figshare.14763051.v1/tiny-data.txt"
+>>> # Not using with Pooch.fetch so no need to pass an instance of Pooch
+>>> downloader(url=url, output_file="tiny-data.txt", pooch=None)
+>>> os.path.exists("tiny-data.txt")
+True
+>>> with open("tiny-data.txt") as f:
+...     print(f.read().strip())
+# A tiny data file for test purposes only
+1  2  3  4  5  6
+>>> os.remove("tiny-data.txt")
+
+
+

Same thing but for our Zenodo archive:

+
>>> url = "doi:10.5281/zenodo.4924875/tiny-data.txt"
+>>> downloader(url=url, output_file="tiny-data.txt", pooch=None)
+>>> os.path.exists("tiny-data.txt")
+True
+>>> with open("tiny-data.txt") as f:
+...     print(f.read().strip())
+# A tiny data file for test purposes only
+1  2  3  4  5  6
+>>> os.remove("tiny-data.txt")
+
+
+

Methods Summary

+ ++++ + + + + + +

DOIDownloader.__call__(url, output_file, pooch)

Download the given DOI URL over HTTP to the given output file.

+
+ +
+
+
+DOIDownloader.__call__(url, output_file, pooch)[source]
+

Download the given DOI URL over HTTP to the given output file.

+

Uses the repository’s API to determine the actual HTTP download URL +from the given DOI.

+

Uses requests.get.

+
+
Parameters
+
    +
  • url (str) – The URL to the file you want to download.

  • +
  • output_file (str or file-like object) – Path (and file name) to which the file will be downloaded.

  • +
  • pooch (Pooch) – The instance of Pooch that is calling this method.

  • +
+
+
+
+ +
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/api/generated/pooch.Decompress.html b/v1.8.0/api/generated/pooch.Decompress.html new file mode 100644 index 00000000..ea12fb1f --- /dev/null +++ b/v1.8.0/api/generated/pooch.Decompress.html @@ -0,0 +1,574 @@ + + + + + + + + + + pooch.Decompress | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

pooch.Decompress

+ +
+
+ +
+
+
+ +
+ +
+

pooch.Decompress

+
+
+class pooch.Decompress(method='auto', name=None)[source]
+

Processor that decompress a file and returns the decompressed version.

+

Use with pooch.Pooch.fetch or pooch.retrieve to decompress +a downloaded data file so that it can be easily opened. Useful for data +files that take a long time to decompress (exchanging disk space for +speed).

+

Supported decompression methods are LZMA (.xz), bzip2 (.bz2), and +gzip (.gz).

+

File names with the standard extensions (see above) can use +method="auto" to automatically determine the compression method. This +can be overwritten by setting the method argument.

+
+

Note

+

To unpack zip and tar archives with one or more files, use +pooch.Unzip and pooch.Untar instead.

+
+

The output file is {fname}.decomp by default but it can be changed by +setting the name parameter.

+
+

Warning

+

Passing in name can cause existing data to be lost! For example, if +a file already exists with the specified name it will be overwritten +with the new decompressed file content. Use this option with +caution.

+
+
+
Parameters
+
    +
  • method (str) – Name of the compression method. Can be “auto”, “lzma”, “xz”, “bzip2”, +or “gzip”.

  • +
  • name (None or str) – Defines the decompressed file name. The file name will be +{fname}.decomp if None (default) or the given name otherwise. +Note that the name should not include the full (or relative) path, +it should be just the file name itself.

  • +
+
+
+

Methods Summary

+ ++++ + + + + + +

Decompress.__call__(fname, action, pooch)

Decompress the given file.

+
+ +
+
+
+Decompress.__call__(fname, action, pooch)[source]
+

Decompress the given file.

+

The output file will be either {fname}.decomp or the given name +class attribute.

+
+
Parameters
+
    +
  • fname (str) – Full path of the compressed file in local storage.

  • +
  • action (str) –

    Indicates what action was taken by pooch.Pooch.fetch or +pooch.retrieve:

    +
      +
    • "download": File didn’t exist locally and was downloaded

    • +
    • "update": Local file was outdated and was re-download

    • +
    • "fetch": File exists and is updated so it wasn’t downloaded

    • +
    +

  • +
  • pooch (pooch.Pooch) – The instance of pooch.Pooch that is calling this.

  • +
+
+
Returns
+

fname (str) – The full path to the decompressed file.

+
+
+
+ +
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/api/generated/pooch.FTPDownloader.html b/v1.8.0/api/generated/pooch.FTPDownloader.html new file mode 100644 index 00000000..bf7d515f --- /dev/null +++ b/v1.8.0/api/generated/pooch.FTPDownloader.html @@ -0,0 +1,559 @@ + + + + + + + + + + pooch.FTPDownloader | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

pooch.FTPDownloader

+ +
+
+ +
+
+
+ +
+ +
+

pooch.FTPDownloader

+
+
+class pooch.FTPDownloader(port=21, username='anonymous', password='', account='', timeout=None, progressbar=False, chunk_size=1024)[source]
+

Download manager for fetching files over FTP.

+

When called, downloads the given file URL into the specified local file. +Uses the ftplib module to manage downloads.

+

Use with pooch.Pooch.fetch or pooch.retrieve to customize +the download of files (for example, to use authentication or print a +progress bar).

+
+
Parameters
+
    +
  • port (int) – Port used for the FTP connection.

  • +
  • username (str) – User name used to login to the server. Only needed if the server +requires authentication (i.e., no anonymous FTP).

  • +
  • password (str) – Password used to login to the server. Only needed if the server +requires authentication (i.e., no anonymous FTP). Use the empty string +to indicate no password is required.

  • +
  • account (str) – Some servers also require an “account” name for authentication.

  • +
  • timeout (int) – Timeout in seconds for ftp socket operations, use None to mean no +timeout.

  • +
  • progressbar (bool) – If True, will print a progress bar of the download to standard error +(stderr). Requires tqdm to be +installed. Custom progress bars are not yet supported.

  • +
  • chunk_size (int) – Files are streamed chunk_size bytes at a time instead of loading +everything into memory at one. Usually doesn’t need to be changed.

  • +
+
+
+

Methods Summary

+ ++++ + + + + + +

FTPDownloader.__call__(url, output_file, pooch)

Download the given URL over FTP to the given output file.

+
+ +
+
+
+FTPDownloader.__call__(url, output_file, pooch, check_only=False)[source]
+

Download the given URL over FTP to the given output file.

+
+
Parameters
+
    +
  • url (str) – The URL to the file you want to download.

  • +
  • output_file (str or file-like object) – Path (and file name) to which the file will be downloaded.

  • +
  • pooch (Pooch) – The instance of Pooch that is calling this method.

  • +
  • check_only (bool) – If True, will only check if a file exists on the server and +without downloading the file. Will return True if the file +exists and False otherwise.

  • +
+
+
Returns
+

availability (bool or None) – If check_only==True, returns a boolean indicating if the file +is available on the server. Otherwise, returns None.

+
+
+
+ +
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/api/generated/pooch.HTTPDownloader.html b/v1.8.0/api/generated/pooch.HTTPDownloader.html new file mode 100644 index 00000000..1c61edc7 --- /dev/null +++ b/v1.8.0/api/generated/pooch.HTTPDownloader.html @@ -0,0 +1,602 @@ + + + + + + + + + + pooch.HTTPDownloader | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

pooch.HTTPDownloader

+ +
+
+ +
+
+
+ +
+ +
+

pooch.HTTPDownloader

+
+
+class pooch.HTTPDownloader(progressbar=False, chunk_size=1024, **kwargs)[source]
+

Download manager for fetching files over HTTP/HTTPS.

+

When called, downloads the given file URL into the specified local file. +Uses the requests library to manage downloads.

+

Use with pooch.Pooch.fetch or pooch.retrieve to customize +the download of files (for example, to use authentication or print a +progress bar).

+
+
Parameters
+
    +
  • progressbar (bool or an arbitrary progress bar object) – If True, will print a progress bar of the download to standard error +(stderr). Requires tqdm to be +installed. Alternatively, an arbitrary progress bar object can be +passed. See Using custom progress bars for details.

  • +
  • chunk_size (int) – Files are streamed chunk_size bytes at a time instead of loading +everything into memory at one. Usually doesn’t need to be changed.

  • +
  • **kwargs – All keyword arguments given when creating an instance of this class +will be passed to requests.get.

  • +
+
+
+

Examples

+

Download one of the data files from the Pooch repository:

+
>>> import os
+>>> from pooch import __version__, check_version
+>>> url = "https://github.com/fatiando/pooch/raw/{}/data/tiny-data.txt"
+>>> url = url.format(check_version(__version__, fallback="main"))
+>>> downloader = HTTPDownloader()
+>>> # Not using with Pooch.fetch so no need to pass an instance of Pooch
+>>> downloader(url=url, output_file="tiny-data.txt", pooch=None)
+>>> os.path.exists("tiny-data.txt")
+True
+>>> with open("tiny-data.txt") as f:
+...     print(f.read().strip())
+# A tiny data file for test purposes only
+1  2  3  4  5  6
+>>> os.remove("tiny-data.txt")
+
+
+

Authentication can be handled by passing a user name and password to +requests.get. All arguments provided when creating an instance of +the class are forwarded to requests.get. We’ll use +auth=(username, password) to use basic HTTPS authentication. The +https://httpbin.org website allows us to make a fake a login request using +whatever username and password we provide to it:

+
>>> user = "doggo"
+>>> password = "goodboy"
+>>> # httpbin will ask for the user and password we provide in the URL
+>>> url = f"https://httpbin.org/basic-auth/{user}/{password}"
+>>> # Trying without the login credentials causes an error
+>>> downloader = HTTPDownloader()
+>>> try:
+...     downloader(url=url, output_file="tiny-data.txt", pooch=None)
+... except Exception:
+...     print("There was an error!")
+There was an error!
+>>> # Pass in the credentials to HTTPDownloader
+>>> downloader = HTTPDownloader(auth=(user, password))
+>>> downloader(url=url, output_file="tiny-data.txt", pooch=None)
+>>> with open("tiny-data.txt") as f:
+...     for line in f:
+...         print(line.rstrip())
+{
+  "authenticated": true,
+  "user": "doggo"
+}
+>>> os.remove("tiny-data.txt")
+
+
+

Methods Summary

+ ++++ + + + + + +

HTTPDownloader.__call__(url, output_file, pooch)

Download the given URL over HTTP to the given output file.

+
+ +
+
+
+HTTPDownloader.__call__(url, output_file, pooch, check_only=False)[source]
+

Download the given URL over HTTP to the given output file.

+

Uses requests.get.

+
+
Parameters
+
    +
  • url (str) – The URL to the file you want to download.

  • +
  • output_file (str or file-like object) – Path (and file name) to which the file will be downloaded.

  • +
  • pooch (Pooch) – The instance of Pooch that is calling this method.

  • +
  • check_only (bool) – If True, will only check if a file exists on the server and +without downloading the file. Will return True if the file +exists and False otherwise.

  • +
+
+
Returns
+

availability (bool or None) – If check_only==True, returns a boolean indicating if the file +is available on the server. Otherwise, returns None.

+
+
+
+ +
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/api/generated/pooch.Pooch.html b/v1.8.0/api/generated/pooch.Pooch.html new file mode 100644 index 00000000..ac50d2af --- /dev/null +++ b/v1.8.0/api/generated/pooch.Pooch.html @@ -0,0 +1,668 @@ + + + + + + + + + + pooch.Pooch | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

pooch.Pooch

+ +
+
+ +
+
+
+ +
+ +
+

pooch.Pooch

+
+
+class pooch.Pooch(path, base_url, registry=None, urls=None, retry_if_failed=0, allow_updates=True)[source]
+

Manager for a local data storage that can fetch from a remote source.

+

Avoid creating Pooch instances directly. Use pooch.create +instead.

+
+
Parameters
+
    +
  • path (str) – The path to the local data storage folder. The path must exist in the +file system.

  • +
  • base_url (str) – Base URL for the remote data source. All requests will be made relative +to this URL.

  • +
  • registry (dict or None) – A record of the files that are managed by this good boy. Keys should be +the file names and the values should be their hashes. Only files +in the registry can be fetched from the local storage. Files in +subdirectories of path must use Unix-style separators ('/') +even on Windows.

  • +
  • urls (dict or None) – Custom URLs for downloading individual files in the registry. A +dictionary with the file names as keys and the custom URLs as values. +Not all files in registry need an entry in urls. If a file has an +entry in urls, the base_url will be ignored when downloading it in +favor of urls[fname].

  • +
  • retry_if_failed (int) – Retry a file download the specified number of times if it fails because +of a bad connection or a hash mismatch. By default, downloads are only +attempted once (retry_if_failed=0). Initially, will wait for 1s +between retries and then increase the wait time by 1s with each retry +until a maximum of 10s.

  • +
  • allow_updates (bool) – Whether existing files in local storage that have a hash mismatch with +the registry are allowed to update from the remote URL. If False, +any mismatch with hashes in the registry will result in an error. +Defaults to True.

  • +
+
+
+

Methods Summary

+ ++++ + + + + + + + + + + + + + + + + + +

Pooch.fetch(fname[, processor, downloader, ...])

Get the absolute path to a file in the local storage.

Pooch.get_url(fname)

Get the full URL to download a file in the registry.

Pooch.is_available(fname[, downloader])

Check availability of a remote file without downloading it.

Pooch.load_registry(fname)

Load entries from a file and add them to the registry.

Pooch.load_registry_from_doi()

Populate the registry using the data repository API

+
+ +
+
+
+Pooch.fetch(fname, processor=None, downloader=None, progressbar=False)[source]
+

Get the absolute path to a file in the local storage.

+

If it’s not in the local storage, it will be downloaded. If the hash of +the file in local storage doesn’t match the one in the registry, will +download a new copy of the file. This is considered a sign that the +file was updated in the remote storage. If the hash of the downloaded +file still doesn’t match the one in the registry, will raise an +exception to warn of possible file corruption.

+

Post-processing actions sometimes need to be taken on downloaded files +(unzipping, conversion to a more efficient format, etc). If these +actions are time or memory consuming, it would be best to do this only +once right after the file is downloaded. Use the processor argument +to specify a function that is executed after the download to perform +these actions. See Processors: Post-download actions for details.

+

Custom file downloaders can be provided through the downloader +argument. By default, Pooch will determine the download protocol from +the URL in the registry. If the server for a given file requires +authentication (username and password), use a downloader that support +these features. Downloaders can also be used to print custom messages +(like a progress bar), etc. See Downloaders: Customizing the download for details.

+
+
Parameters
+
    +
  • fname (str) – The file name (relative to the base_url of the remote data +storage) to fetch from the local storage.

  • +
  • processor (None or callable) – If not None, then a function (or callable object) that will be +called before returning the full path and after the file has been +downloaded. See Processors: Post-download actions for details.

  • +
  • downloader (None or callable) – If not None, then a function (or callable object) that will be +called to download a given URL to a provided local file name. See +Downloaders: Customizing the download for details.

  • +
  • progressbar (bool or an arbitrary progress bar object) – If True, will print a progress bar of the download to standard +error (stderr). Requires tqdm to +be installed. Alternatively, an arbitrary progress bar object can +be passed. See Using custom progress bars for details.

  • +
+
+
Returns
+

full_path (str) – The absolute path (including the file name) of the file in the +local storage.

+
+
+
+ +
+
+Pooch.get_url(fname)[source]
+

Get the full URL to download a file in the registry.

+
+
Parameters
+

fname (str) – The file name (relative to the base_url of the remote data +storage) to fetch from the local storage.

+
+
+
+ +
+
+Pooch.is_available(fname, downloader=None)[source]
+

Check availability of a remote file without downloading it.

+

Use this method when working with large files to check if they are +available for download.

+
+
Parameters
+
    +
  • fname (str) – The file name (relative to the base_url of the remote data +storage).

  • +
  • downloader (None or callable) – If not None, then a function (or callable object) that will be +called to check the availability of the file on the server. See +Downloaders: Customizing the download for details.

  • +
+
+
Returns
+

status (bool) – True if the file is available for download. False otherwise.

+
+
+
+ +
+
+Pooch.load_registry(fname)[source]
+

Load entries from a file and add them to the registry.

+

Use this if you are managing many files.

+

Each line of the file should have file name and its hash separated by +a space. Hash can specify checksum algorithm using “alg:hash” format. +In case no algorithm is provided, SHA256 is used by default. +Only one file per line is allowed. Custom download URLs for individual +files can be specified as a third element on the line. Line comments +can be added and must be prepended with #.

+
+
Parameters
+

fname (str | fileobj) – Path (or open file object) to the registry file.

+
+
+
+ +
+
+Pooch.load_registry_from_doi()[source]
+

Populate the registry using the data repository API

+

Fill the registry with all the files available in the data repository, +along with their hashes. It will make a request to the data repository +API to retrieve this information. No file is downloaded during this +process.

+
+

Important

+

This method is intended to be used only when the base_url is +a DOI.

+
+
+ +
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/api/generated/pooch.SFTPDownloader.html b/v1.8.0/api/generated/pooch.SFTPDownloader.html new file mode 100644 index 00000000..9cc01469 --- /dev/null +++ b/v1.8.0/api/generated/pooch.SFTPDownloader.html @@ -0,0 +1,553 @@ + + + + + + + + + + pooch.SFTPDownloader | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

pooch.SFTPDownloader

+ +
+
+ +
+
+
+ +
+ +
+

pooch.SFTPDownloader

+
+
+class pooch.SFTPDownloader(port=22, username='anonymous', password='', account='', timeout=None, progressbar=False)[source]
+

Download manager for fetching files over SFTP.

+

When called, downloads the given file URL into the specified local file. +Requires paramiko to be +installed.

+

Use with pooch.Pooch.fetch or pooch.retrieve to customize +the download of files (for example, to use authentication or print a +progress bar).

+
+
Parameters
+
    +
  • port (int) – Port used for the SFTP connection.

  • +
  • username (str) – User name used to login to the server. Only needed if the server +requires authentication (i.e., no anonymous SFTP).

  • +
  • password (str) – Password used to login to the server. Only needed if the server +requires authentication (i.e., no anonymous SFTP). Use the empty +string to indicate no password is required.

  • +
  • timeout (int) – Timeout in seconds for sftp socket operations, use None to mean no +timeout.

  • +
  • progressbar (bool or an arbitrary progress bar object) – If True, will print a progress bar of the download to standard +error (stderr). Requires tqdm to +be installed.

  • +
+
+
+

Methods Summary

+ ++++ + + + + + +

SFTPDownloader.__call__(url, output_file, pooch)

Download the given URL over SFTP to the given output file.

+
+ +
+
+
+SFTPDownloader.__call__(url, output_file, pooch)[source]
+

Download the given URL over SFTP to the given output file.

+

The output file must be given as a string (file name/path) and not an +open file object! Otherwise, paramiko cannot save to that file.

+
+
Parameters
+
    +
  • url (str) – The URL to the file you want to download.

  • +
  • output_file (str) – Path (and file name) to which the file will be downloaded. Cannot +be a file object.

  • +
  • pooch (Pooch) – The instance of Pooch that is calling this method.

  • +
+
+
+
+ +
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/api/generated/pooch.Untar.html b/v1.8.0/api/generated/pooch.Untar.html new file mode 100644 index 00000000..b4707fcb --- /dev/null +++ b/v1.8.0/api/generated/pooch.Untar.html @@ -0,0 +1,557 @@ + + + + + + + + + + pooch.Untar | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

pooch.Untar

+ +
+
+ +
+
+
+ +
+ +
+

pooch.Untar

+
+
+class pooch.Untar(members=None, extract_dir=None)[source]
+

Processor that unpacks a tar archive and returns a list of all files.

+

Use with pooch.Pooch.fetch or pooch.retrieve to untar a +downloaded data file into a folder in the local data store. The +method/function will return a list with the names of the extracted files +instead of the archive.

+

The output folder is {fname}.untar.

+
+
Parameters
+
    +
  • members (list or None) – If None, will unpack all files in the archive. Otherwise, members +must be a list of file names to unpack from the archive. Only these +files will be unpacked.

  • +
  • extract_dir (str or None) – If None, files will be unpacked to the default location (a folder in +the same location as the downloaded tar file, with the suffix +.untar added). Otherwise, files will be unpacked to +extract_dir, which is interpreted as a relative path (relative to +the cache location provided by pooch.retrieve or +pooch.Pooch.fetch).

  • +
+
+
+

Methods Summary

+ ++++ + + + + + +

Untar.__call__(fname, action, pooch)

Extract all files from the given archive.

+
+ +
+
+
+Untar.__call__(fname, action, pooch)
+

Extract all files from the given archive.

+
+
Parameters
+
    +
  • fname (str) – Full path of the zipped file in local storage.

  • +
  • action (str) –

    Indicates what action was taken by pooch.Pooch.fetch or +pooch.retrieve:

    +
      +
    • "download": File didn’t exist locally and was downloaded

    • +
    • "update": Local file was outdated and was re-download

    • +
    • "fetch": File exists and is updated so it wasn’t downloaded

    • +
    +

  • +
  • pooch (pooch.Pooch) – The instance of pooch.Pooch that is calling this.

  • +
+
+
Returns
+

fnames (list of str) – A list of the full path to all files in the extracted archive.

+
+
+
+ +
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/api/generated/pooch.Unzip.html b/v1.8.0/api/generated/pooch.Unzip.html new file mode 100644 index 00000000..59268b2f --- /dev/null +++ b/v1.8.0/api/generated/pooch.Unzip.html @@ -0,0 +1,557 @@ + + + + + + + + + + pooch.Unzip | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

pooch.Unzip

+ +
+
+ +
+
+
+ +
+ +
+

pooch.Unzip

+
+
+class pooch.Unzip(members=None, extract_dir=None)[source]
+

Processor that unpacks a zip archive and returns a list of all files.

+

Use with pooch.Pooch.fetch or pooch.retrieve to unzip a +downloaded data file into a folder in the local data store. The +method/function will return a list with the names of the unzipped files +instead of the zip archive.

+

The output folder is {fname}.unzip.

+
+
Parameters
+
    +
  • members (list or None) – If None, will unpack all files in the zip archive. Otherwise, members +must be a list of file names to unpack from the archive. Only these +files will be unpacked.

  • +
  • extract_dir (str or None) – If None, files will be unpacked to the default location (a folder in +the same location as the downloaded zip file, with the suffix +.unzip added). Otherwise, files will be unpacked to +extract_dir, which is interpreted as a relative path (relative to +the cache location provided by pooch.retrieve or +pooch.Pooch.fetch).

  • +
+
+
+

Methods Summary

+ ++++ + + + + + +

Unzip.__call__(fname, action, pooch)

Extract all files from the given archive.

+
+ +
+
+
+Unzip.__call__(fname, action, pooch)
+

Extract all files from the given archive.

+
+
Parameters
+
    +
  • fname (str) – Full path of the zipped file in local storage.

  • +
  • action (str) –

    Indicates what action was taken by pooch.Pooch.fetch or +pooch.retrieve:

    +
      +
    • "download": File didn’t exist locally and was downloaded

    • +
    • "update": Local file was outdated and was re-download

    • +
    • "fetch": File exists and is updated so it wasn’t downloaded

    • +
    +

  • +
  • pooch (pooch.Pooch) – The instance of pooch.Pooch that is calling this.

  • +
+
+
Returns
+

fnames (list of str) – A list of the full path to all files in the extracted archive.

+
+
+
+ +
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/api/generated/pooch.check_version.html b/v1.8.0/api/generated/pooch.check_version.html new file mode 100644 index 00000000..b27671b1 --- /dev/null +++ b/v1.8.0/api/generated/pooch.check_version.html @@ -0,0 +1,529 @@ + + + + + + + + + + pooch.check_version | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

pooch.check_version

+ +
+
+ +
+
+
+ +
+ +
+

pooch.check_version

+
+
+pooch.check_version(version, fallback='master')[source]
+

Check if a version is PEP440 compliant and there are no unreleased changes.

+

For example, version = "0.1" will be returned as is but version = +"0.1+10.8dl8dh9" will return the fallback. This is the convention used by +versioneer to mark that +this version is 10 commits ahead of the last release.

+
+
Parameters
+
    +
  • version (str) – A version string.

  • +
  • fallback (str) – What to return if the version string has unreleased changes.

  • +
+
+
Returns
+

version (str) – If version is PEP440 compliant and there are unreleased changes, then +return version. Otherwise, return fallback.

+
+
Raises
+

InvalidVersion – If version is not PEP440 compliant.

+
+
+

Examples

+
>>> check_version("0.1")
+'0.1'
+>>> check_version("0.1a10")
+'0.1a10'
+>>> check_version("0.1+111.9hdg36")
+'master'
+>>> check_version("0.1+111.9hdg36", fallback="dev")
+'dev'
+
+
+
+ +
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/api/generated/pooch.create.html b/v1.8.0/api/generated/pooch.create.html new file mode 100644 index 00000000..c7c00903 --- /dev/null +++ b/v1.8.0/api/generated/pooch.create.html @@ -0,0 +1,631 @@ + + + + + + + + + + pooch.create | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

pooch.create

+ +
+
+ +
+
+
+ +
+ +
+

pooch.create

+
+
+pooch.create(path, base_url, version=None, version_dev='master', env=None, registry=None, urls=None, retry_if_failed=0, allow_updates=True)[source]
+

Create a Pooch with sensible defaults to fetch data files.

+

If a version string is given, the Pooch will be versioned, meaning that the +local storage folder and the base URL depend on the project version. This +is necessary if your users have multiple versions of your library installed +(using virtual environments) and you updated the data files between +versions. Otherwise, every time a user switches environments would trigger +a re-download of the data. The version string will be appended to the local +storage path (for example, ~/.mypooch/cache/v0.1) and inserted into the +base URL (for example, +https://github.com/fatiando/pooch/raw/v0.1/data). If the version string +contains +XX.XXXXX, it will be interpreted as a development version.

+

Does not create the local data storage folder. The folder will only be +created the first time a download is attempted with +pooch.Pooch.fetch. This makes it safe to use this function at the +module level (so it’s executed on import and the resulting +Pooch is a global variable).

+
+
Parameters
+
    +
  • path (str, PathLike, list or tuple) – The path to the local data storage folder. If this is a list or tuple, +we’ll join the parts with the appropriate separator. The version will +be appended to the end of this path. Use pooch.os_cache for a +sensible default.

  • +
  • base_url (str) – Base URL for the remote data source. All requests will be made relative +to this URL. The string should have a {version} formatting mark in +it. We will call .format(version=version) on this string. If the +URL does not end in a '/', a trailing '/' will be added +automatically.

  • +
  • version (str or None) – The version string for your project. Should be PEP440 compatible. If +None is given, will not attempt to format base_url and no subfolder +will be appended to path.

  • +
  • version_dev (str) – The name used for the development version of a project. If your data is +hosted on Github (and base_url is a Github raw link), then +"master" is a good choice (default). Ignored if version is None.

  • +
  • env (str or None) – An environment variable that can be used to overwrite path. This +allows users to control where they want the data to be stored. We’ll +append version to the end of this value as well.

  • +
  • registry (dict or None) – A record of the files that are managed by this Pooch. Keys should be +the file names and the values should be their hashes. Only files +in the registry can be fetched from the local storage. Files in +subdirectories of path must use Unix-style separators ('/') +even on Windows.

  • +
  • urls (dict or None) – Custom URLs for downloading individual files in the registry. A +dictionary with the file names as keys and the custom URLs as values. +Not all files in registry need an entry in urls. If a file has an +entry in urls, the base_url will be ignored when downloading it in +favor of urls[fname].

  • +
  • retry_if_failed (int) – Retry a file download the specified number of times if it fails because +of a bad connection or a hash mismatch. By default, downloads are only +attempted once (retry_if_failed=0). Initially, will wait for 1s +between retries and then increase the wait time by 1s with each retry +until a maximum of 10s.

  • +
  • allow_updates (bool or str) – Whether existing files in local storage that have a hash mismatch with +the registry are allowed to update from the remote URL. If a string is +passed, we will assume it’s the name of an environment variable that +will be checked for the true/false value. If False, any mismatch +with hashes in the registry will result in an error. Defaults to +True.

  • +
+
+
Returns
+

pooch (Pooch) – The Pooch initialized with the given arguments.

+
+
+

Examples

+

Create a Pooch for a release (v0.1):

+
>>> pup = create(path="myproject",
+...              base_url="http://some.link.com/{version}/",
+...              version="v0.1",
+...              registry={"data.txt": "9081wo2eb2gc0u..."})
+>>> print(pup.path.parts)  # The path is a pathlib.Path
+('myproject', 'v0.1')
+>>> # The local folder is only created when a dataset is first downloaded
+>>> print(pup.path.exists())
+False
+>>> print(pup.base_url)
+http://some.link.com/v0.1/
+>>> print(pup.registry)
+{'data.txt': '9081wo2eb2gc0u...'}
+>>> print(pup.registry_files)
+['data.txt']
+
+
+

If this is a development version (12 commits ahead of v0.1), then the +version_dev will be used (defaults to "master"):

+
>>> pup = create(path="myproject",
+...              base_url="http://some.link.com/{version}/",
+...              version="v0.1+12.do9iwd")
+>>> print(pup.path.parts)
+('myproject', 'master')
+>>> print(pup.base_url)
+http://some.link.com/master/
+
+
+

Versioning is optional (but highly encouraged):

+
>>> pup = create(path="myproject",
+...              base_url="http://some.link.com/",
+...              registry={"data.txt": "9081wo2eb2gc0u..."})
+>>> print(pup.path.parts)  # The path is a pathlib.Path
+('myproject',)
+>>> print(pup.base_url)
+http://some.link.com/
+
+
+

To place the storage folder at a subdirectory, pass in a list and we’ll +join the path for you using the appropriate separator for your operating +system:

+
>>> pup = create(path=["myproject", "cache", "data"],
+...              base_url="http://some.link.com/{version}/",
+...              version="v0.1")
+>>> print(pup.path.parts)
+('myproject', 'cache', 'data', 'v0.1')
+
+
+

The user can overwrite the storage path by setting an environment variable:

+
>>> # The variable is not set so we'll use *path*
+>>> pup = create(path=["myproject", "not_from_env"],
+...              base_url="http://some.link.com/{version}/",
+...              version="v0.1",
+...              env="MYPROJECT_DATA_DIR")
+>>> print(pup.path.parts)
+('myproject', 'not_from_env', 'v0.1')
+>>> # Set the environment variable and try again
+>>> import os
+>>> os.environ["MYPROJECT_DATA_DIR"] = os.path.join("myproject", "env")
+>>> pup = create(path=["myproject", "not_env"],
+...              base_url="http://some.link.com/{version}/",
+...              version="v0.1",
+...              env="MYPROJECT_DATA_DIR")
+>>> print(pup.path.parts)
+('myproject', 'env', 'v0.1')
+
+
+
+ +
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/api/generated/pooch.file_hash.html b/v1.8.0/api/generated/pooch.file_hash.html new file mode 100644 index 00000000..69286f7a --- /dev/null +++ b/v1.8.0/api/generated/pooch.file_hash.html @@ -0,0 +1,521 @@ + + + + + + + + + + pooch.file_hash | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

pooch.file_hash

+ +
+
+ +
+
+
+ +
+ +
+

pooch.file_hash

+
+
+pooch.file_hash(fname, alg='sha256')[source]
+

Calculate the hash of a given file.

+

Useful for checking if a file has changed or been corrupted.

+
+
Parameters
+
    +
  • fname (str) – The name of the file.

  • +
  • alg (str) – The type of the hashing algorithm

  • +
+
+
Returns
+

hash (str) – The hash of the file.

+
+
+

Examples

+
>>> fname = "test-file-for-hash.txt"
+>>> with open(fname, "w") as f:
+...     __ = f.write("content of the file")
+>>> print(file_hash(fname))
+0fc74468e6a9a829f103d069aeb2bb4f8646bad58bf146bb0e3379b759ec4a00
+>>> import os
+>>> os.remove(fname)
+
+
+
+ +
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/api/generated/pooch.get_logger.html b/v1.8.0/api/generated/pooch.get_logger.html new file mode 100644 index 00000000..9bffc164 --- /dev/null +++ b/v1.8.0/api/generated/pooch.get_logger.html @@ -0,0 +1,507 @@ + + + + + + + + + + pooch.get_logger | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

pooch.get_logger

+ +
+
+ +
+
+
+ +
+ +
+

pooch.get_logger

+
+
+pooch.get_logger()[source]
+

Get the default event logger.

+

The logger records events like downloading files, unzipping archives, etc. +Use the method logging.Logger.setLevel of this object to adjust the +verbosity level from Pooch.

+
+
Returns
+

logger (logging.Logger) – The logger object for Pooch

+
+
+
+ +
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/api/generated/pooch.make_registry.html b/v1.8.0/api/generated/pooch.make_registry.html new file mode 100644 index 00000000..1c2ac698 --- /dev/null +++ b/v1.8.0/api/generated/pooch.make_registry.html @@ -0,0 +1,512 @@ + + + + + + + + + + pooch.make_registry | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

pooch.make_registry

+ +
+
+ +
+
+
+ +
+ +
+

pooch.make_registry

+
+
+pooch.make_registry(directory, output, recursive=True)[source]
+

Make a registry of files and hashes for the given directory.

+

This is helpful if you have many files in your test dataset as it keeps you +from needing to manually update the registry.

+
+
Parameters
+
    +
  • directory (str) – Directory of the test data to put in the registry. All file names in +the registry will be relative to this directory.

  • +
  • output (str) – Name of the output registry file.

  • +
  • recursive (bool) – If True, will recursively look for files in subdirectories of +directory.

  • +
+
+
+
+ +
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/api/generated/pooch.os_cache.html b/v1.8.0/api/generated/pooch.os_cache.html new file mode 100644 index 00000000..488ccfb9 --- /dev/null +++ b/v1.8.0/api/generated/pooch.os_cache.html @@ -0,0 +1,518 @@ + + + + + + + + + + pooch.os_cache | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

pooch.os_cache

+ +
+
+ +
+
+
+ +
+ +
+

pooch.os_cache

+
+
+pooch.os_cache(project)[source]
+

Default cache location based on the operating system.

+

The folder locations are defined by the platformdirs package +using the user_cache_dir function. +Usually, the locations will be following (see the +platformdirs documentation):

+
    +
  • Mac: ~/Library/Caches/<AppName>

  • +
  • Unix: ~/.cache/<AppName> or the value of the XDG_CACHE_HOME +environment variable, if defined.

  • +
  • Windows: C:\Users\<user>\AppData\Local\<AppAuthor>\<AppName>\Cache

  • +
+
+
Parameters
+

project (str) – The project name.

+
+
Returns
+

cache_path (pathlib.Path) – The default location for the data cache. User directories ('~') are +not expanded.

+
+
+
+ +
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/api/generated/pooch.retrieve.html b/v1.8.0/api/generated/pooch.retrieve.html new file mode 100644 index 00000000..860b2072 --- /dev/null +++ b/v1.8.0/api/generated/pooch.retrieve.html @@ -0,0 +1,655 @@ + + + + + + + + + + pooch.retrieve | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

pooch.retrieve

+ +
+
+ +
+
+
+ +
+ +
+

pooch.retrieve

+
+
+pooch.retrieve(url, known_hash, fname=None, path=None, processor=None, downloader=None, progressbar=False)[source]
+

Download and cache a single file locally.

+

Uses HTTP or FTP by default, depending on the protocol in the given url. +Other download methods can be controlled through the downloader argument +(see below).

+

The file will be downloaded to a temporary location first and its hash will +be compared to the given known_hash. This is done to ensure that the +download happened correctly and securely. If the hash doesn’t match, the +file will be deleted and an exception will be raised.

+

If the file already exists locally, its hash will be compared to +known_hash. If they are not the same, this is interpreted as the file +needing to be updated and it will be downloaded again.

+

You can bypass these checks by passing known_hash=None. If this is +done, the SHA256 hash of the downloaded file will be logged to the screen. +It is highly recommended that you copy and paste this hash as known_hash +so that future downloads are guaranteed to be the exact same file. This is +crucial for reproducible computations.

+

If the file exists in the given path with the given fname and the hash +matches, it will not be downloaded and the absolute path to the file will +be returned.

+
+

Note

+

This function is meant for downloading single files. If you need to +manage the download and caching of several files, with versioning, use +pooch.create and pooch.Pooch instead.

+
+
+
Parameters
+
    +
  • url (str) – The URL to the file that is to be downloaded. Ideally, the URL should +end in a file name.

  • +
  • known_hash (str or None) – A known hash (checksum) of the file. Will be used to verify the +download or check if an existing file needs to be updated. By default, +will assume it’s a SHA256 hash. To specify a different hashing method, +prepend the hash with algorithm:, for example +md5:pw9co2iun29juoh or sha1:092odwhi2ujdp2du2od2odh2wod2. If +None, will NOT check the hash of the downloaded file or check if an +existing file needs to be updated.

  • +
  • fname (str or None) – The name that will be used to save the file. Should NOT include the +full path, just the file name (it will be appended to path). If +None, will create a unique file name using a combination of the last +part of the URL (assuming it’s the file name) and the MD5 hash of the +URL. For example, 81whdo2d2e928yd1wi22-data-file.csv. This ensures +that files from different URLs never overwrite each other, even if they +have the same name.

  • +
  • path (str or PathLike or None) – The location of the cache folder on disk. This is where the file will +be saved. If None, will save to a pooch folder in the default cache +location for your operating system (see pooch.os_cache).

  • +
  • processor (None or callable) – If not None, then a function (or callable object) that will be called +before returning the full path and after the file has been downloaded +(if required). See Processors: Post-download actions for details.

  • +
  • downloader (None or callable) – If not None, then a function (or callable object) that will be called +to download a given URL to a provided local file name. See +Downloaders: Customizing the download for details.

  • +
  • progressbar (bool or an arbitrary progress bar object) – If True, will print a progress bar of the download to standard error +(stderr). Requires tqdm to be +installed. Alternatively, an arbitrary progress bar object can be +passed. See Using custom progress bars for details.

  • +
+
+
Returns
+

full_path (str) – The absolute path (including the file name) of the file in the local +storage.

+
+
+

Examples

+

Download one of the data files from the Pooch repository on GitHub:

+
>>> import os
+>>> from pooch import __version__, check_version, retrieve
+>>> # Make a URL for the version of pooch we have installed
+>>> url = "https://github.com/fatiando/pooch/raw/{}/data/tiny-data.txt"
+>>> url = url.format(check_version(__version__, fallback="main"))
+>>> # Download the file and save it locally. Will check the MD5 checksum of
+>>> # the downloaded file against the given value to make sure it's the
+>>> # right file. You can use other hashes by specifying different
+>>> # algorithm names (sha256, sha1, etc).
+>>> fname = retrieve(
+...     url, known_hash="md5:70e2afd3fd7e336ae478b1e740a5f08e",
+... )
+>>> with open(fname) as f:
+...     print(f.read().strip())
+# A tiny data file for test purposes only
+1  2  3  4  5  6
+>>> # Running again won't trigger a download and only return the path to
+>>> # the existing file.
+>>> fname2 = retrieve(
+...     url, known_hash="md5:70e2afd3fd7e336ae478b1e740a5f08e",
+... )
+>>> print(fname2 == fname)
+True
+>>> os.remove(fname)
+
+
+

Files that are compressed with gzip, xz/lzma, or bzip2 can be automatically +decompressed by passing using the pooch.Decompress processor:

+
>>> from pooch import Decompress
+>>> # URLs to a gzip compressed version of the data file.
+>>> url = ("https://github.com/fatiando/pooch/raw/{}/"
+...        + "pooch/tests/data/tiny-data.txt.gz")
+>>> url = url.format(check_version(__version__, fallback="main"))
+>>> # By default, you would have to decompress the file yourself
+>>> fname = retrieve(
+...     url,
+...     known_hash="md5:8812ba10b6c7778014fdae81b03f9def",
+... )
+>>> print(os.path.splitext(fname)[1])
+.gz
+>>> # Use the processor to decompress after download automatically and
+>>> # return the path to the decompressed file instead.
+>>> fname2 = retrieve(
+...     url,
+...     known_hash="md5:8812ba10b6c7778014fdae81b03f9def",
+...     processor=Decompress(),
+... )
+>>> print(fname2 == fname)
+False
+>>> with open(fname2) as f:
+...     print(f.read().strip())
+# A tiny data file for test purposes only
+1  2  3  4  5  6
+>>> os.remove(fname)
+>>> os.remove(fname2)
+
+
+

When downloading archives (zip or tar), it can be useful to unpack them +after download to avoid having to do that yourself. Use the processors +pooch.Unzip or pooch.Untar to do this automatically:

+
>>> from pooch import Unzip
+>>> # URLs to a zip archive with a single data file.
+>>> url = ("https://github.com/fatiando/pooch/raw/{}/"
+...        + "pooch/tests/data/tiny-data.zip")
+>>> url = url.format(check_version(__version__, fallback="main"))
+>>> # By default, you would get the path to the archive
+>>> fname = retrieve(
+...     url,
+...     known_hash="md5:e9592cb46cf3514a1079051f8a148148",
+... )
+>>> print(os.path.splitext(fname)[1])
+.zip
+>>> os.remove(fname)
+>>> # Using the processor, the archive will be unzipped and a list with the
+>>> # path to every file will be returned instead of a single path.
+>>> fnames = retrieve(
+...     url,
+...     known_hash="md5:e9592cb46cf3514a1079051f8a148148",
+...     processor=Unzip(),
+... )
+>>> # There was only a single file in our archive.
+>>> print(len(fnames))
+1
+>>> with open(fnames[0]) as f:
+...     print(f.read().strip())
+# A tiny data file for test purposes only
+1  2  3  4  5  6
+>>> for f in fnames:
+...     os.remove(f)
+
+
+
+ +
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/api/generated/pooch.test.html b/v1.8.0/api/generated/pooch.test.html new file mode 100644 index 00000000..25693ad7 --- /dev/null +++ b/v1.8.0/api/generated/pooch.test.html @@ -0,0 +1,515 @@ + + + + + + + + + + pooch.test | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

pooch.test

+ +
+
+ +
+
+
+ +
+ +
+

pooch.test

+
+
+pooch.test(doctest=True, verbose=True, coverage=False)[source]
+

Run the test suite.

+

Uses py.test to discover and run the tests.

+
+
Parameters
+
    +
  • doctest (bool) – If True, will run the doctests as well (code examples that start +with a >>> in the docs).

  • +
  • verbose (bool) – If True, will print extra information during the test run.

  • +
  • coverage (bool) – If True, will run test coverage analysis on the code as well. +Requires pytest-cov.

  • +
+
+
Raises
+

AssertionError – If pytest returns a non-zero error code indicating that some tests have + failed.

+
+
+
+ +
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/api/index.html b/v1.8.0/api/index.html new file mode 100644 index 00000000..0f774c94 --- /dev/null +++ b/v1.8.0/api/index.html @@ -0,0 +1,670 @@ + + + + + + + + + + List of functions and classes (API) | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+ Contents +
+ +
+
+
+
+
+ +
+

List of functions and classes (API)

+ +
+
+ +
+

Contents

+
+ +
+
+
+ +
+ +
+

List of functions and classes (API)

+
+

Note

+

All functions and classes should be accessed from the pooch +top-level namespace.

+

Modules inside of the pooch package are meant mostly for internal +organization. Please avoid importing directly from submodules since +functions/classes may be moved around.

+
+
+

Core

+ ++++ + + + + + + + + + + + +

pooch.create(path, base_url[, version, ...])

Create a Pooch with sensible defaults to fetch data files.

pooch.Pooch(path, base_url[, registry, ...])

Manager for a local data storage that can fetch from a remote source.

pooch.retrieve(url, known_hash[, fname, ...])

Download and cache a single file locally.

+
+
+

Utilities

+ ++++ + + + + + + + + + + + + + + + + + +

pooch.os_cache(project)

Default cache location based on the operating system.

pooch.make_registry(directory, output[, ...])

Make a registry of files and hashes for the given directory.

pooch.file_hash(fname[, alg])

Calculate the hash of a given file.

pooch.check_version(version[, fallback])

Check if a version is PEP440 compliant and there are no unreleased changes.

pooch.get_logger()

Get the default event logger.

+
+
+

Downloaders

+ ++++ + + + + + + + + + + + + + + +

pooch.HTTPDownloader([progressbar, chunk_size])

Download manager for fetching files over HTTP/HTTPS.

pooch.FTPDownloader([port, username, ...])

Download manager for fetching files over FTP.

pooch.SFTPDownloader([port, username, ...])

Download manager for fetching files over SFTP.

pooch.DOIDownloader([progressbar, chunk_size])

Download manager for fetching files from Digital Object Identifiers (DOIs).

+
+
+

Processors

+ ++++ + + + + + + + + + + + +

pooch.Unzip([members, extract_dir])

Processor that unpacks a zip archive and returns a list of all files.

pooch.Untar([members, extract_dir])

Processor that unpacks a tar archive and returns a list of all files.

pooch.Decompress([method, name])

Processor that decompress a file and returns the decompressed version.

+
+
+

Miscellaneous

+ ++++ + + + + + +

pooch.test([doctest, verbose, coverage])

Run the test suite.

+
+
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/authentication.html b/v1.8.0/authentication.html new file mode 100644 index 00000000..422d2d8d --- /dev/null +++ b/v1.8.0/authentication.html @@ -0,0 +1,598 @@ + + + + + + + + + + Authentication | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+ Contents +
+ +
+
+
+
+
+ +
+

Authentication

+ +
+
+ +
+

Contents

+
+ +
+
+
+ +
+ +
+

Authentication

+
+

HTTP authentication

+

Use the HTTPDownloader class directly to provide login +credentials to HTTP servers that require basic authentication. For example:

+
from pooch import HTTPDownloader
+
+
+def fetch_protected_data():
+    """
+    Fetch a file from a server that requires authentication
+    """
+    # Let the downloader know the login credentials
+    download_auth = HTTPDownloader(auth=("my_username", "my_password"))
+    fname = GOODBOY.fetch("some-data.csv", downloader=download_auth)
+    data = pandas.read_csv(fname)
+    return data
+
+
+

It’s probably not a good idea to hard-code credentials in your code. One way +around this is to ask users to set their own credentials through environment +variables. The download code could look something like so:

+
import os
+
+
+def fetch_protected_data():
+    """
+    Fetch a file from a server that requires authentication
+    """
+    # Get the credentials from the user's environment
+    username = os.environ.get("SOMESITE_USERNAME")
+    password = os.environ.get("SOMESITE_PASSWORD")
+    # Let the downloader know the login credentials
+    download_auth = HTTPDownloader(auth=(username, password))
+    fname = GOODBOY.fetch("some-data.csv", downloader=download_auth)
+    data = pandas.read_csv(fname)
+    return data
+
+
+
+
+

FTP/SFTP with authentication

+

Pooch also comes with the FTPDownloader and +SFTPDownloader downloaders that can be used +when files are distributed over FTP or SFTP (secure FTP).

+
+

Note

+

To download files over SFTP, +paramiko needs to be installed.

+
+

Sometimes the FTP server doesn’t support anonymous FTP and needs authentication +or uses a non-default port. +In these cases, pass in the downloader class explicitly (works with both FTP +and SFTP):

+
import os
+
+
+def fetch_c137():
+    """
+    Load the C-137 sample data as a pandas.DataFrame (over FTP this time).
+    """
+    username = os.environ.get("MYDATASERVER_USERNAME")
+    password = os.environ.get("MYDATASERVER_PASSWORD")
+    download_ftp = pooch.FTPDownloader(username=username, password=password)
+    fname = GOODBOY.fetch("c137.csv", downloader=download_ftp)
+    data = pandas.read_csv(fname)
+    return data
+
+
+
+
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/changes.html b/v1.8.0/changes.html new file mode 100644 index 00000000..a63b8006 --- /dev/null +++ b/v1.8.0/changes.html @@ -0,0 +1,1525 @@ + + + + + + + + + + Changelog | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ + +
+
+ + + +
+ +
+

Changelog

+
+

Version 1.8.0

+

Released on: 2023/10/24

+

doi:10.5281/zenodo.10037888

+

Bug fixes:

+
    +
  • Fix bug: add support for old and new Zenodo APIs (#375)

  • +
+

New features:

+
    +
  • Only create local data directories if necessary (#370)

  • +
  • Speed up import time by lazy loading requests (#328)

  • +
+

Maintenance:

+
    +
  • Add support for Python 3.11 (#348)

  • +
  • Only run CI cron job for the upstream repository (#361)

  • +
+

Documentation:

+
    +
  • Add GemGIS to list of projects using Pooch (#349)

  • +
  • Fix spelling of Dataverse (#353)

  • +
  • Fix grammar on retrieve documentation (#359)

  • +
+

This release contains contributions from:

+
    +
  • Hugo van Kemenade

  • +
  • AlexanderJuestel

  • +
  • Mark Harfouche

  • +
  • Philip Durbin

  • +
  • Rob Luke

  • +
  • Santiago Soler

  • +
  • Stephan Hoyer

  • +
+
+
+

Version 1.7.0

+

Released on: 2023/02/27

+

doi:10.5281/zenodo.7678844

+

Bug fixes:

+
    +
  • Make archive extraction always take members into account (#316)

  • +
  • Figshare downloaders fetch the correct version, instead of always the latest one. (#343)

  • +
+

New features:

+
    +
  • Allow spaces in filenames in registry files (#315)

  • +
  • Refactor Pooch.is_available to use downloaders (#322)

  • +
  • Add support for downloading files from Dataverse DOIs (#318)

  • +
  • Add a new Pooch.load_registry_from_doi method that populates the Pooch registry using DOI-based data repositories (#325)

  • +
  • Support urls for Zenodo repositories created through the GitHub integration service, which include slashes in the filename of the main zip files (#340)

  • +
  • Automatically add a trailing slash to base_url on pooch.create (#344)

  • +
+

Maintenance:

+
    +
  • Drop support for Python 3.6 (#299)

  • +
  • Port from deprecated appdirs to platformdirs (#339)

  • +
  • Update version of Codecov’s Action to v3 (#345)

  • +
+

Documentation:

+
    +
  • Update sphinx, theme, and sphinx-panels (#300)

  • +
  • Add CITATION.cff for the JOSS article (#308)

  • +
  • Use Markdown for the README (#311)

  • +
  • Improve docstring of known_hash in retrieve function (#333)

  • +
  • Replace link to Pooch’s citation with a BibTeX code snippet (#335)

  • +
+

Projects that started using Pooch:

+ +

This release contains contributions from:

+
    +
  • Alex Fikl

  • +
  • Anirudh Dagar

  • +
  • Björn Ludwig

  • +
  • Brian Rose

  • +
  • Dominic Kempf

  • +
  • Florian Wellmann

  • +
  • Gabriel Fu

  • +
  • Kyle I S Harrington

  • +
  • Leonardo Uieda

  • +
  • myd7349

  • +
  • Rowan Cockett

  • +
  • Santiago Soler

  • +
+
+
+

Version 1.6.0

+

Released on: 2022/01/24

+

doi:10.5281/zenodo.5793074

+
+

Warning

+

Pooch v1.6.0 is the last release that is compatible with Python 3.6.

+
+

Important notes:

+
    +
  • Pooch now specifies version bounds for our required dependencies and a plan for dropping support for older versions. Please revise it if you depend on Pooch.

  • +
+

Enhancements:

+
    +
  • Add option to disable updates on hash mismatch (#291 and #292)

  • +
  • Allow enabling progress bars with an argument in Pooch.fetch and retrieve (#277)

  • +
+

Documentation:

+
    +
  • Use real data URLs in the README example code (#295)

  • +
  • Tell users to import from the top-level namespace (#288)

  • +
  • Update the contact link to fatiando.org/contact (#282)

  • +
  • Refer the community guides to fatiando/community (#281)

  • +
  • Mention in docs that figshare collections aren’t supported (#275)

  • +
+

Maintenance:

+
    +
  • Replace Google Analytics for Plausible to make our docs more privacy-friendly (#293)

  • +
  • Use Dependente to capture dependencies on CI (#289)

  • +
  • Use build instead of setup.py (#287)

  • +
  • Run the tests weekly on GitHub Actions (#286)

  • +
  • Set minimum required version of dependencies (#280)

  • +
  • Rename “master” to “main” throughout the project (#278)

  • +
  • Remove trailing slash from GitHub handle in AUTHORS.md (#279)

  • +
+

This release contains contributions from:

+
    +
  • Santiago Soler

  • +
  • Genevieve Buckley

  • +
  • Ryan Abernathey

  • +
  • Ryan May

  • +
  • Leonardo Uieda

  • +
+
+
+

Version 1.5.2

+

Released on: 2021/10/11

+

doi:10.5281/zenodo.5560923

+

Bug fixes:

+
    +
  • Fix bug when unpacking an entire subfolder from an archive. Now both unpacking processors (Untar and Unzip) handle members that are folders (not files) correctly. (#266)

  • +
+

Enhancements:

+
    +
  • Add support for Python 3.10 (#260)

  • +
  • Point to the user’s code for the file_hash warning instead of our internal code (which isn’t very useful) (#259)

  • +
+

Documentation:

+
    +
  • Fix typo in a variable name of the examples in the documentation (#268)

  • +
  • Fix typo when specifying the SFTP protocol in the about page (#267)

  • +
+

Maintenance:

+
    +
  • Remove old testing checks if running on TravisCI (#265)

  • +
+

This release contains contributions from:

+
    +
  • Santiago Soler

  • +
  • Hugo van Kemenade

  • +
  • Mark Harfouche

  • +
  • Leonardo Uieda

  • +
+
+
+

Version 1.5.1

+

Released on: 2021/08/24

+

doi:10.5281/zenodo.5242882

+
+

Warning

+

Please use from pooch import file_hash instead of from +pooch.utils import file_hash. This is backwards compatible with all +previous versions of Pooch. We recommend importing all functions and +classes from the top-level namespace.

+
+

Bug fixes:

+
    +
  • Make file_hash accessible from the pooch.utils module again. Moving +this function to pooch.hashes caused crashes downstream. To prevent these +crashes, add a wrapper back to utils that issues a warning that users should +import from the top-level namespace instead. +(#257)

  • +
  • Use a mirror of the test data directory in tests that write to it. +(#255)

  • +
  • Add a pytest mark for tests accessing the network so that they can easily +excluded when testing offline. (#254)

  • +
+

This release contains contributions from:

+
    +
  • Antonio Valentino

  • +
  • Leonardo Uieda

  • +
+
+
+

Version 1.5.0

+

Released on: 2021/08/23

+

doi:10.5281/zenodo.5235242

+

New features:

+
    +
  • Add support for non-cryptographic hashes from the xxhash package. They aren’t +as safe (but safe enough) and compute in fractions of the time from SHA or +MD5. This makes it feasible to use hash checking on large datasets. (#242)

  • +
  • Add support for using figshare and Zenodo DOIs as URLs (with the protocol +doi:{DOI}/{file name}, which works out-of-the-box with Pooch.fetch +and retrieve). Can only download 1 file from the archive (not the full +archive) and the file name must be specified in the URL. (#241)

  • +
+

Maintenance:

+
    +
  • Move hash functions to their own private module. No changes to the public +API. (#244)

  • +
  • Run CI jobs on Python version extremes instead of all supported versions +(#243)

  • +
+

This release contains contributions from:

+
    +
  • Mark Harfouche

  • +
  • Leonardo Uieda

  • +
+
+
+

Version 1.4.0

+

Released on: 2021/06/08

+

doi:10.5281/zenodo.4914758

+

Bug fixes:

+
    +
  • Fix bug in Untar and Unzip when the archive contains subfolders +(#224)

  • +
+

Documentation:

+
    +
  • New theme (sphinx-book-theme) and layout of the documentation (#236 #237 #238)

  • +
+

Enhancements:

+
    +
  • Add support for non-tqdm progress bars on HTTPDownloader (#228)

  • +
  • Allow custom unpack locations in Untar and Unzip (#224)

  • +
+

Maintenance:

+
    +
  • Replace versioneer with setuptools-scm (#235)

  • +
  • Automatically check license notice on code files (#231)

  • +
  • Don’t store documentation HTML as CI build artifacts (#221)

  • +
+

This release contains contributions from:

+
    +
  • Leonardo Uieda

  • +
  • Agustina Pesce

  • +
  • Clément Robert

  • +
  • Daniel McCloy

  • +
+
+
+

Version 1.3.0

+

Released on: 2020/11/27

+Digital Object Identifier for the Zenodo archive +

Bug fixes:

+
    +
  • Properly handle capitalized hashes. On Windows, users might sometimes get +capitalized hashes from the system. To avoid false hash mismatches, convert +stored and computed hashes to lowercase before doing comparisons. Convert +hashes to lowercase when reading from the registry to make sure stored hashes +are always lowercase. (#214)

  • +
+

New features:

+
    +
  • Add option to retry downloads if they fail. The new retry_if_failed +option to pooch.create and pooch.Pooch allows retrying the download +the specified number of times in case of failures due to hash mismatches +(coming from Pooch) or network issues (coming from requests). This is +useful for running downloads on CI that tend to fail sporadically. Waits a +period of time between consecutive downloads starting with 1s and increasing +up to 10s in 1s increments. (#215)

  • +
  • Allow user defined decompressed file names. Introduce new name argument +to pooch.Decompress to allow user defined file names. Defaults to the +previous naming convention for backward compatibility. (#203)

  • +
+

Documentation:

+
    +
  • Add seaborn-image to list of packages using Pooch (#218)

  • +
+

Maintenance:

+
    +
  • Add support for Python 3.9. (#220)

  • +
  • Drop support for Python 3.5. (#204)

  • +
  • Use pip instead of conda to speed up Actions (#216)

  • +
  • Add license and copyright notice to every .py file (#213)

  • +
+

This release contains contributions from:

+
    +
  • Leonardo Uieda

  • +
  • Danilo Horta

  • +
  • Hugo van Kemenade

  • +
  • SarthakJariwala

  • +
+
+
+

Version 1.2.0

+

Released on: 2020/09/10

+Digital Object Identifier for the Zenodo archive +
+

Warning

+

Pooch v1.2.0 is the last release that is compatible with Python 3.5.

+
+

Bug fixes:

+
    +
  • Fix FTP availability check when the file is in a directory. If the data file +is not in the base directory, the Pooch.is_available test was broken +since we were checking for the full path in ftp.nlst instead of just the +file name. (#191)

  • +
+

New features:

+
    +
  • Add the SFTPDownloader class for secure FTP downloads (#165)

  • +
  • Expose Pooch version as pooch.__version__ (#179)

  • +
  • Allow line comments in registry files with # (#180)

  • +
+

Enhancements:

+
    +
  • Point to Unzip/tar from Decompress docs and errors (#200)

  • +
+

Documentation:

+
    +
  • Re-factor the documentation into separate pages (#202)

  • +
  • Add warning to the docs about dropping Python 3.5 (#201)

  • +
  • Add histolab to the Pooch-powered +projects (#189)

  • +
+

Maintenance:

+
    +
  • Push documentation to GitHub Pages using Actions (#198)

  • +
  • Add GitHub Actions workflow for publishing to PyPI (#196)

  • +
  • Set up GitHub Actions for testing and linting (#194)

  • +
  • Test FTP downloads using a local test server (#192)

  • +
+

This release contains contributions from:

+
    +
  • Leonardo Uieda

  • +
  • Hugo van Kemenade

  • +
  • Alessia Marcolini

  • +
  • Luke Gregor

  • +
  • Mathias Hauser

  • +
+
+
+

Version 1.1.1

+

Released on: 2020/05/14

+Digital Object Identifier for the Zenodo archive +

Bug fixes:

+
    +
  • Delay data cache folder creation until the first download is attempted. As +seen in recent issues in scikit-image, creating the +data folder in pooch.create can cause problems since this function is +called at import time. This means that importing the package in parallel can +cause race conditions and crashes. To prevent that from happening, delay the +creation of the cache folder until Pooch.fetch or retrieve are +called. +(#173)

  • +
  • Allow the data folder to already exist when creating it. This is can help +cope with parallel execution as well. +(#171)

  • +
+

Documentation:

+
    +
  • Added scikit-image to list of Pooch users. +(#168)

  • +
  • Fix typo in README and front page contributing section. +(#166)

  • +
+

This release contains contributions from:

+
    +
  • Leonardo Uieda

  • +
  • Egor Panfilov

  • +
  • Rowan Cockett

  • +
+
+
+

Version 1.1.0

+

Released on: 2020/04/13

+Digital Object Identifier for the Zenodo archive +

New features:

+
    +
  • New function pooch.retrieve to fetch single files This is much more +convenient than setting up a Pooch while retaining the hash checks and +use of downloaders and processors. It automatically selects a unique file +name and saves files to a cache folder. +(#152)

  • +
  • Allow to use of different hashing algorithms (other than SHA256). Optionally +specify the hash as alg:hash and allow pooch.Pooch to recognize the +algorithm when comparing hashes. Setting an algorithsm is optional and +omiting it defaults to SHA256. This is particularly useful when data are +coming from external sources and published hashes are already available. +(#133)

  • +
+

Documentation:

+
    +
  • Add example for fetching datasets that change on the server, for which the +hash check would always fail. +(#144)

  • +
  • Fix path examples in docstring of pooch.os_cache. The docstring mentioned +the data path as examples instead of the cache path. +(#140)

  • +
  • Add example of creating a registry when you don’t have the data files locally +and would have to download them manually. The example uses the +pooch.retrieve function to automate the process. The example covers two +cases: when all remote files share the same base URL and when every file has +its own URL. +(#161)

  • +
+

Maintenance:

+
    +
  • A lot of general refactoring of the internals of Pooch to facilitate +development of the new pooch.retrieve function +(#159 +#157 +#156 +#151 +#149)

  • +
+

This release contains contributions from:

+
    +
  • Leonardo Uieda

  • +
  • Santiago Soler

  • +
  • Kacper Kowalik

  • +
  • Lucas Martin-King

  • +
  • Zac Flamig

  • +
+
+
+

Version 1.0.0

+

Released on: 2020/01/28

+Digital Object Identifier for the Zenodo archive +

This release marks the stabilization of the Pooch API. Further changes to the +1.* line will be fully backwards compatible (meaning that updating Pooch should +not break existing code). If there is great need to make backwards incompatible +changes, we will release a 2.* line. In that case, bug fixes will still be +ported to the 1.* line for a period of time.

+

Improvements:

+
    +
  • Allow blank lines in registry files. Previously, they would cause an error. +(#138)

  • +
+

Backwards incompatible changes:

+
    +
  • Using Python’s logging module to instead of warnings to inform users +of download, update, and decompression/unpacking actions. This allows +messages to be logged with different priorities and the user filter out log +messages or silence Pooch entirely. Introduces the function +pooch.get_logger to access the logging object used by Pooch. Users +who relied on Pooch issuing warnings will need to update to capturing logs +instead. All other parts of the API remain unchanged. +(#115)

  • +
+

This release contains contributions from:

+
    +
  • Daniel Shapero

  • +
+
+
+

Version 0.7.2

+

Released on: 2020/01/17

+

🚨 Announcement: 🚨 +We now have a JOSS paper about Pooch! +Please cite it when you use Pooch for your research. +(#116 with reviews in +#132 and +#134)

+

This is minor release which only updates the citation information to +the new JOSS paper. No DOI was issued for this release since there are +no code or documentation changes.

+
+
+

Version 0.7.1

+

Released on: 2020/01/17

+Digital Object Identifier for the Zenodo archive +

Improvements:

+
    +
  • Better error messages when hashes don’t match. Include the file name in the +exception for a hash mismatch between a downloaded file and the registry. +Before, we included the name of temporary file, which wasn’t very +informative. +(#128)

  • +
  • Better error message for malformed registry files. When loading a registry +file, inform the name of the file and include the offending content in the +error message instead of just the line number. +(#129)

  • +
+

Maintenance:

+
    +
  • Change development status flag in setup.py to “stable” instead of +“alpha”. +(#127)

  • +
+

This release was reviewed at the Journal of Open Source Software. The code and +software paper contain contributions from:

+
    +
  • Anderson Banihirwe

  • +
  • Martin Durant

  • +
  • Mark Harfouche

  • +
  • Hugo van Kemenade

  • +
  • John Leeman

  • +
  • Rémi Rampin

  • +
  • Daniel Shapero

  • +
  • Santiago Rubén Soler

  • +
  • Matthew Turk

  • +
  • Leonardo Uieda

  • +
+
+
+

Version 0.7.0

+

Released on: 2019/11/19

+Digital Object Identifier for the Zenodo archive +

New features:

+
    +
  • New pooch.FTPDownloader class for downloading files over FTP. Uses the +standard library ftplib. The appropriate downloader is automatically +selected by pooch.Pooch.fetch based on the URL (for anonymous FTP only), +so no configuration is required. +If authentication is required, pooch.FTPDownloader provides the need +support. Ported from +NCAR/aletheia-data by the author. +(#118)

  • +
  • Support for file-like objects to Pooch.load_registry (opened either in +binary or text mode). +(#117)

  • +
+

Maintenance:

+
    +
  • Testing and official support for Python 3.8. +(#113)

  • +
  • 🚨 Drop support for Python 2.7. 🚨 Remove conditional dependencies and CI +jobs. +(#100)

  • +
+

Documentation:

+
    +
  • In the tutorial, use pkg_resources.resource_stream() from setuptools to +load the registry.txt file. It’s less error-prone than using os.path +and __file__ and allows the package to work from zip files. +(#120)

  • +
  • Docstrings formatted to 79 characters (instead of 88) for better rendering in +Jupyter notebooks and IPython. These displays are limited to 80 chars so the +longer lines made the docstring unreadable. +(#123)

  • +
+

This release contains contributions from:

+
    +
  • Anderson Banihirwe

  • +
  • Hugo van Kemenade

  • +
  • Remi Rampin

  • +
  • Leonardo Uieda

  • +
+
+
+

Version 0.6.0

+

Released on: 2019/10/22

+Digital Object Identifier for the Zenodo archive +

🚨 Pooch v0.6.0 is the last release to support Python 2.7 🚨

+

New features:

+
    +
  • Add optional download progress bar to pooch.HTTPDownloader +(#97)

  • +
+

Maintenance:

+
    +
  • Warn that 0.6.0 is the last version to support Python 2.7 +(#108)

  • +
+

Documentation:

+
    +
  • Update contact information to point to our Slack channel +(#107)

  • +
  • Add icepack to list of projects using Pooch +(#98)

  • +
+

This release contains contributions from:

+
    +
  • Daniel Shapero

  • +
  • Leonardo Uieda

  • +
+
+
+

Version 0.5.2

+

Released on: 2019/06/24

+

Maintenance:

+
    +
  • Add back support for Python 3.5 with continuous integration tests. No code changes +were needed, only removing the restriction from setup.py. +(#93)

  • +
+

This release contains contributions from:

+
    +
  • Leonardo Uieda

  • +
+
+
+

Version 0.5.1

+

Released on: 2019/05/21

+

Documentation fixes:

+
    +
  • Fix formatting error in pooch.Decompress docstring. +(#81)

  • +
  • Fix wrong imports in the usage guide for post-processing hooks. +(#84)

  • +
  • Add section to the usage guide explaining when to use pooch.Decompress. +(#85)

  • +
+

This release contains contributions from:

+
    +
  • Santiago Soler

  • +
  • Leonardo Uieda

  • +
+
+
+

Version 0.5.0

+

Released on: 2019/05/20

+

New features:

+
    +
  • New processor pooch.Decompress saves a decompressed version of the downloaded +file. Supports gzip, lzma/xz, and bzip2 compression. Note: Under Python 2.7, lzma +and bzip2 require the backports.lzma and bz2file packages as well. These are +soft dependencies and not required to use Pooch. See Installing. (#78)

  • +
  • New processor pooch.Untar unpacks files contained in a downloaded tar archive +(with or without compression). (#77)

  • +
+

This release contains contributions from:

+
    +
  • Matthew Turk

  • +
  • Leonardo Uieda

  • +
+
+
+

Version 0.4.0

+

Released on: 2019/05/01

+

New features:

+
    +
  • Add customizable downloaders. Delegate file download into separate classes that can be +passed to Pooch.fetch. Created the HTTPDownloader class (used by default) +which can also be used to download files that require authentication/login. (#66)

  • +
  • Add post-download processor hooks to Pooch.fetch. Allows users to pass in a +function that is executed right before returning and can overwrite the file path that +is returned by fetch. Use this, for example, to perform unpacking/decompression +operations on larger files that can be time consuming and we only want to do once. +(#59)

  • +
  • Add the Unzip post-download processor to extract files from a downloaded zip +archive. Unpacks files into a directory in the local store and returns a list of all +unzipped files. (#72)

  • +
  • Make the check_version function public. It’s used internally but will be useful in +examples that want to download things from the pooch repository. (#69)

  • +
+

Maintenance:

+
    +
  • Pin sphinx to version 1.8.5. New versions of Sphinx (2.0.*) are messing up the +numpydoc style docstrings. (#64)

  • +
+

This release contains contributions from:

+
    +
  • Santiago Soler

  • +
  • Leonardo Uieda

  • +
+
+
+

Version 0.3.1

+

Released on: 2019/03/28

+

Minor patches:

+
    +
  • Add a project logo (#57)

  • +
  • Replace http with https in the README.rst to avoid mixed content warnings +in some browsers (#56)

  • +
+
+
+

Version 0.3.0

+

Released on: 2019/03/27

+

New features:

+
    +
  • Use the appdirs library to get the cache directory. Could change the default +data location on all platforms. Locations are compatible with the +XDG Base Directory Specification +(#45)

  • +
  • Add method Pooch.is_available to check remote file availability +(#50)

  • +
  • Add Pooch.registry_files property to get a name of all files in the registry +(#42)

  • +
  • Make Pooch.get_url a public method to get the download URL for a given file +(#55)

  • +
+

Maintenance:

+
    +
  • Drop support for Python 3.5. Pooch now requires Python >= 3.6. +(#52)

  • +
  • Add a private method to check if a file is in the registry (#49)

  • +
  • Fix typo in the Pooch.load_registry docstring (#41)

  • +
+

This release contains contributions from:

+
    +
  • Santiago Soler

  • +
  • Rémi Rampin

  • +
  • Leonardo Uieda

  • +
+
+
+

Version 0.2.1

+

Released on: 2018/11/15

+

Bug fixes:

+
    +
  • Fix unwanted ~ directory creation when not using a version in pooch.create +(#37)

  • +
+
+
+

Version 0.2.0

+

Released on: 2018/10/31

+

Bug fixes:

+
    +
  • Avoid copying of files across the file system (#33)

  • +
  • Correctly delete temporary downloads on error (#32)

  • +
+

New features:

+
    +
  • Allow custom download URLs for individual files (#30)

  • +
  • Allow dataset versioning to be optional (#29)

  • +
+

Maintenance:

+
    +
  • Move URLs building to a dedicated method for easy subclassing (#31)

  • +
  • Add testing and support for Python 3.7 (#25)

  • +
+
+
+

Version 0.1.1

+

Released on: 2018/08/30

+

Bug fixes:

+
    +
  • Check if the local data folder is writable and warn the user instead of crashing +(#23)

  • +
+
+
+

Version 0.1

+

Released on: 2018/08/20

+
    +
  • Fist release of Pooch. Manages downloading sample data files over HTTP from a server +and storing them in a local directory. Main features:

    +
      +
    • Download a file only if it’s not in the local storage.

    • +
    • Check the SHA256 hash to make sure the file is not corrupted or needs updating.

    • +
    • If the hash is different from the registry, Pooch will download a new version of +the file.

    • +
    • If the hash still doesn’t match, Pooch will raise an exception warning of possible +data corruption.

    • +
    +
  • +
+
+
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/citing.html b/v1.8.0/citing.html new file mode 100644 index 00000000..f6a50b62 --- /dev/null +++ b/v1.8.0/citing.html @@ -0,0 +1,520 @@ + + + + + + + + + + Citing Pooch | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

Citing Pooch

+ +
+
+ +
+
+
+ +
+ +
+

Citing Pooch

+

This is research software made by scientists. Citations help us justify the +effort that goes into building and maintaining this project.

+

If you used Pooch in your research, please consider citing our paper:

+
+

Uieda, L., Soler, S.R., Rampin, R., van Kemenade, H., Turk, M., Shapero, +D., Banihirwe, A., and Leeman, J. (2020). Pooch: A friend to fetch your +data files. Journal of Open Source Software, 5(45), 1943. +doi:10.21105/joss.01943

+
+

This is an open-access publication. The paper and the associated software +review can be freely accessed at: https://doi.org/10.21105/joss.01943

+

Here is a Bibtex entry to make things easier if you’re using Latex:

+
@article{uieda2020,
+  title = {{Pooch}: {A} friend to fetch your data files},
+  author = {Leonardo Uieda and Santiago Soler and R{\'{e}}mi Rampin and Hugo van Kemenade and Matthew Turk and Daniel Shapero and Anderson Banihirwe and John Leeman},
+  year = {2020},
+  doi = {10.21105/joss.01943},
+  url = {https://doi.org/10.21105/joss.01943},
+  month = jan,
+  publisher = {The Open Journal},
+  volume = {5},
+  number = {45},
+  pages = {1943},
+  journal = {Journal of Open Source Software}
+}
+
+
+
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/compatibility.html b/v1.8.0/compatibility.html new file mode 100644 index 00000000..76ca534d --- /dev/null +++ b/v1.8.0/compatibility.html @@ -0,0 +1,610 @@ + + + + + + + + + + Version compatibility | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + + +
+
+
+
+ +
+

Version compatibility

+ +
+ +
+
+ +
+ +
+

Version compatibility

+
+

Pooch backwards incompatible changes

+

We try to retain backwards compatibility whenever possible. Major breaking +changes to the Pooch API will be marked by a major release and deprecation +warnings will be issued in previous releases to give developers ample time to +adapt.

+

If there are any backwards incompatible changes, they will be listed below:

+ +++++ + + + + + + + + + + +

Version introduced

Severity

Notes

v1.0.0

Low

We replaced use of warning with the logging module for all +messages issued by Pooch. This allows messages to be logged with +different priorities and the user filter out log messages or silence +Pooch entirely. Users who relied on Pooch issuing warnings will need +to update to capturing logs instead. The vast majority of users are +unaffected.

+
+
+

Supported dependency versions

+

Pooch follows the recommendations in +NEP29 for setting +the minimum required version of our dependencies. +In short, we support all minor releases of our dependencies from the previous +24 months before a Pooch release with a minimum of 2 minor releases.

+

We follow this guidance conservatively and won’t require newer versions if the +older ones are still working without causing problems. +Whenever support for a version is dropped, we will include a note in the +Changelog.

+
+

Note

+

This was introduced in Pooch v1.6.0.

+
+
+
+

Supported Python versions

+

If you require support for older Python versions, please pin Pooch to the +following releases to ensure compatibility:

+ ++++ + + + + + + + + + + + + + + +

Python version

Last compatible Pooch release

2.7

0.6.0

3.5

1.2.0

3.6

1.6.0

+
+
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/decompressing.html b/v1.8.0/decompressing.html new file mode 100644 index 00000000..4c108349 --- /dev/null +++ b/v1.8.0/decompressing.html @@ -0,0 +1,540 @@ + + + + + + + + + + Decompressing | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

Decompressing

+ +
+
+ +
+
+
+ +
+ +
+

Decompressing

+

If you have a compressed file that is not an archive (zip or tar), you can use +pooch.Decompress to decompress it after download.

+

For example, large binary files can be compressed with gzip to reduce +download times but will need to be decompressed before loading, which can be +slow. +You can trade storage space for speed by keeping a decompressed copy of the +file:

+
from pooch import Decompress
+
+def fetch_compressed_file():
+    """
+    Load a large binary file that has been gzip compressed.
+    """
+    # Pass in the processor to decompress the file on download
+    fname = GOODBOY.fetch("large-binary-file.npy.gz", processor=Decompress())
+    # The file returned is the decompressed version which can be loaded by
+    # numpy
+    data = numpy.load(fname)
+    return data
+
+
+

pooch.Decompress returns "large-binary-file.npy.gz.decomp" as the +decompressed file name by default. +You can change this behaviour by passing a file name instead:

+
import os
+from pooch import Decompress
+
+def fetch_compressed_file():
+    """
+    Load a large binary file that has been gzip compressed.
+    """
+    # Pass in the processor to decompress the file on download
+    fname = GOODBOY.fetch("large-binary-file.npy.gz",
+        processor=Decompress(name="a-different-file-name.npy"),
+    )
+    # The file returned is now named "a-different-file-name.npy"
+    data = numpy.load(fname)
+    return data
+
+
+
+

Warning

+

Passing in name can cause existing data to be lost! +For example, if a file already exists with the specified name it will be +overwritten with the new decompressed file content. +Use this option with caution.

+
+
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/downloaders.html b/v1.8.0/downloaders.html new file mode 100644 index 00000000..8747f47b --- /dev/null +++ b/v1.8.0/downloaders.html @@ -0,0 +1,639 @@ + + + + + + + + + + Downloaders: Customizing the download | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+ Contents +
+ +
+
+
+
+
+ +
+

Downloaders: Customizing the download

+ +
+
+ +
+

Contents

+
+ +
+
+
+ +
+ +
+

Downloaders: Customizing the download

+

By default, pooch.Pooch.fetch and pooch.retrieve will detect +the download protocol from the given URL (HTTP, FTP, SFTP, DOI) and use the +appropriate download method. +Sometimes this is not enough: some servers require logins, redirections, or +other non-standard operations. +To get around this, use the downloader argument of +fetch and retrieve.

+

Downloaders are Python callable objects (like functions or classes with a +__call__ method) and must have the following format:

+
def mydownloader(url, output_file, pooch):
+    '''
+    Download a file from the given URL to the given local file.
+
+    The function **must** take the following arguments (in order).
+
+    Parameters
+    ----------
+    url : str
+        The URL to the file you want to download.
+    output_file : str or file-like object
+        Path (and file name) to which the file will be downloaded.
+    pooch : pooch.Pooch
+        The instance of the Pooch class that is calling this function.
+
+    No return value is required.
+    '''
+    ...
+
+
+

Pooch provides downloaders for HTTP, FTP, and SFTP that support authentication +and optionally printing progress bars. +See List of functions and classes (API) for a list of available downloaders.

+

Common uses of downloaders include:

+ +
+

Creating your own downloaders

+

If your use case is not covered by our downloaders, you can implement your own. +pooch.Pooch.fetch and pooch.retrieve will accept any callable +obejct that has the signature specified above. As an example, consider the +case in which the login credentials need to be provided to a site that is +redirected from the original download URL:

+
import requests
+
+
+def redirect_downloader(url, output_file, pooch):
+    """
+    Download after following a redirection.
+    """
+    # Get the credentials from the user's environment
+    username = os.environ.get("SOMESITE_USERNAME")
+    password = os.environ.get("SOMESITE_PASSWORD")
+    # Make a request that will redirect to the login page
+    login = requests.get(url)
+    # Provide the credentials and download from the new URL
+    download = HTTPDownloader(auth=(username, password))
+    download(login.url, output_file, mypooch)
+
+
+def fetch_protected_data():
+    """
+    Fetch a file from a server that requires authentication
+    """
+    fname = GOODBOY.fetch("some-data.csv", downloader=redirect_downloader)
+    data = pandas.read_csv(fname)
+    return data
+
+
+
+
+

Availability checks

+

Optionally, downloaders can take a check_only keyword argument (default +to False) that makes them only check if a given file is available for +download without downloading the file. +This makes a downloader compatible with pooch.Pooch.is_available.

+

In this case, the downloader should return a boolean:

+
def mydownloader(url, output_file, pooch, check_only=False):
+    '''
+    Download a file from the given URL to the given local file.
+
+    The function **must** take the following arguments (in order).
+
+    Parameters
+    ----------
+    url : str
+        The URL to the file you want to download.
+    output_file : str or file-like object
+        Path (and file name) to which the file will be downloaded.
+    pooch : pooch.Pooch
+        The instance of the Pooch class that is calling this function.
+    check_only : bool
+        If True, will only check if a file exists on the server and
+        **without downloading the file**. Will return ``True`` if the file
+        exists and ``False`` otherwise.
+
+    Returns
+    -------
+    None or availability
+        If ``check_only==True``, returns a boolean indicating if the file
+        is available on the server. Otherwise, returns ``None``.
+    '''
+    ...
+
+
+

If a downloader does not implement an availability check (i.e., doesn’t take +check_only as a keyword argument), then pooch.Pooch.is_available +will raise a NotImplementedError.

+
+
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/genindex.html b/v1.8.0/genindex.html new file mode 100644 index 00000000..cf7cc55f --- /dev/null +++ b/v1.8.0/genindex.html @@ -0,0 +1,654 @@ + + + + + + + + + + Index | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + +
+ + +
+ +
+
+
+
+ +
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/hashes.html b/v1.8.0/hashes.html new file mode 100644 index 00000000..a61c0653 --- /dev/null +++ b/v1.8.0/hashes.html @@ -0,0 +1,657 @@ + + + + + + + + + + Hashes: Calculating and bypassing | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + + +
+
+
+
+ +
+

Hashes: Calculating and bypassing

+ +
+ +
+
+ +
+ +
+

Hashes: Calculating and bypassing

+

Pooch uses hashes to check if files are up-to-date or possibly +corrupted:

+
    +
  • If a file exists in the local folder, Pooch will check that its hash matches +the one in the registry. If it doesn’t, we’ll assume that it needs to be +updated.

  • +
  • If a file needs to be updated or doesn’t exist, Pooch will download it from +the remote source and check the hash. If the hash doesn’t match, an exception +is raised to warn of possible file corruption.

  • +
  • Cryptographic hashes may be used where users wish to ensure the security of +their download.

  • +
+
+

Calculating hashes

+

You can generate hashes for your data files using openssl in the terminal:

+
$ openssl sha256 data/c137.csv
+SHA256(data/c137.csv)= baee0894dba14b12085eacb204284b97e362f4f3e5a5807693cc90ef415c1b2d
+
+
+

Or using the pooch.file_hash function (which is a convenient way of +calling Python’s hashlib):

+
import pooch
+print(pooch.file_hash("data/c137.csv"))
+
+
+
+
+

Specifying the hash algorithm

+

By default, Pooch uses SHA256 +hashes. +Other hash methods that are available in hashlib can also be used:

+
import pooch
+print(pooch.file_hash("data/c137.csv", alg="sha512"))
+
+
+

In this case, you can specify the hash algorithm in the registry by +prepending it to the hash, for example "md5:0hljc7298ndo2" or +"sha512:803o3uh2pecb2p3829d1bwouh9d". +Pooch will understand this and use the appropriate method.

+
+
+

Bypassing the hash check

+

Sometimes we might not know the hash of the file or it could change on the +server periodically. +To bypass the check, we can set the hash value to None when specifying the +registry argument for pooch.create +(or the known_hash in pooch.retrieve).

+

In this example, we want to use Pooch to download a list of weather stations +around Australia:

+
    +
  • The file with the stations is in an FTP server and we want to store it +locally in separate folders for each day that the code is run.

  • +
  • The problem is that the stations.zip file is updated on the server +instead of creating a new one, so the hash check would fail.

  • +
+

This is how you can solve this problem:

+
import datetime
+import pooch
+
+# Get the current data to store the files in separate folders
+CURRENT_DATE = datetime.datetime.now().date()
+
+GOODBOY = pooch.create(
+    path=pooch.os_cache("bom_daily_stations") / CURRENT_DATE,
+    base_url="ftp://ftp.bom.gov.au/anon2/home/ncc/metadata/sitelists/",
+    registry={
+        "stations.zip": None,
+    },
+)
+
+
+

When running this same code again at a different date, the file will be +downloaded again because the local cache folder changed and the file is no +longer present in it. +If you omit CURRENT_DATE from the cache path, then Pooch will only fetch +the files once, unless they are deleted from the cache.

+
+

Attention

+

If this script is run over a period of time, your cache directory will +increase in size, as the files are stored in daily subdirectories.

+
+
+
+

Other supported hashes

+

Beyond hashing algorithms supported by hashlib, Pooch supports algorithms +provided by the xxhash package. +If the xxhash package is available, users may specify to use one of +the algorithms provided by the package.

+
$ xxh128sum data/store.zip
+6a71973c93eac6c8839ce751ce10ae48  data/store.zip
+$ # ^^^^^^^^^^^^^^^^^^^ The hash  ^^^^^^^^^^^^^^ The filename
+
+
+
import datetime
+import pooch
+
+# Get the current data to store the files in separate folders
+CURRENT_DATE = datetime.datetime.now().date()
+
+GOODBOY = pooch.create(
+    [...],
+    registry={
+        "store.zip": "xxh128:6a71973c93eac6c8839ce751ce10ae48",
+    },
+)
+
+
+
+
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/index.html b/v1.8.0/index.html new file mode 100644 index 00000000..db15e820 --- /dev/null +++ b/v1.8.0/index.html @@ -0,0 +1,626 @@ + + + + + + + + + + Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+ Contents +
+ +
+
+
+
+
+ +
+

+ +
+
+ +
+

Contents

+
+ +
+
+
+ +
+ + + + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/install.html b/v1.8.0/install.html new file mode 100644 index 00000000..8a1f83ab --- /dev/null +++ b/v1.8.0/install.html @@ -0,0 +1,589 @@ + + + + + + + + + + Installing | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+ Contents +
+ +
+
+
+
+
+ +
+

Installing

+ +
+
+ +
+

Contents

+
+ +
+
+
+ +
+ +
+

Installing

+

There are different ways to install Pooch:

+
+ +
+

Using the pip package manager:

+
python -m pip install pooch
+
+
+
+ +
+

Using the conda package manager that comes with the +Anaconda/Miniconda distribution:

+
conda install pooch --channel conda-forge
+
+
+
+ +
+

Using pip to install the latest unreleased version from GitHub +(not recommended in most situations):

+
python -m pip install --upgrade git+https://github.com/fatiando/pooch
+
+
+
+
+
+

Note

+

The commands above should be executed in a terminal. On Windows, use the +cmd.exe or the “Anaconda Prompt” app if you’re using Anaconda.

+
+
+

Which Python?

+

You’ll need Python >= 3.7. See Supported Python versions if you +require support for older versions.

+
+
+

Dependencies

+

The required dependencies should be installed automatically when you install +Pooch using conda or pip. Optional dependencies have to be installed +manually.

+

Required:

+ +

Optional:

+ +
+
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/logging.html b/v1.8.0/logging.html new file mode 100644 index 00000000..5d91c43b --- /dev/null +++ b/v1.8.0/logging.html @@ -0,0 +1,538 @@ + + + + + + + + + + Logging and verbosity | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+ Contents +
+ +
+
+
+
+
+ +
+

Logging and verbosity

+ +
+
+ +
+

Contents

+
+ +
+
+
+ +
+ +
+

Logging and verbosity

+

Pooch uses the logging module to print messages about downloads and +processor execution.

+
+

Adjusting the logging level

+

Pooch will log events like downloading a new file, updating an existing one, or +unpacking an archive by printing to the terminal. +You can change how verbose these events are by getting the event logger from +pooch and changing the logging level:

+
logger = pooch.get_logger()
+logger.setLevel("WARNING")
+
+
+

Most of the events from Pooch are logged at the info level; this code says that +you only care about warnings or errors, like inability to create the data +cache. +The event logger is a logging.Logger object, so you can use that +class’s methods to handle logging events in more sophisticated ways if you +wish.

+
+
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/multiple-files.html b/v1.8.0/multiple-files.html new file mode 100644 index 00000000..faaec800 --- /dev/null +++ b/v1.8.0/multiple-files.html @@ -0,0 +1,652 @@ + + + + + + + + + + Fetching files from a registry | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + + +
+
+
+
+ +
+

Fetching files from a registry

+ +
+ +
+
+ +
+ +
+

Fetching files from a registry

+

If you need to manage the download of multiple files from one or more +locations, then this section is for you!

+
+

Setup

+

In the following example we’ll assume that:

+
    +
  1. You have several data files served from the same base URL (for example, +"https://www.somewebpage.org/science/data").

  2. +
  3. You know the file names and their +hashes.

  4. +
+

We will use pooch.create to set up our download manager:

+
import pooch
+
+
+odie = pooch.create(
+    # Use the default cache folder for the operating system
+    path=pooch.os_cache("my-project"),
+    base_url="https://www.somewebpage.org/science/data/",
+    # The registry specifies the files that can be fetched
+    registry={
+        "temperature.csv": "sha256:19uheidhlkjdwhoiwuhc0uhcwljchw9ochwochw89dcgw9dcgwc",
+        "gravity-disturbance.nc": "sha256:1upodh2ioduhw9celdjhlfvhksgdwikdgcowjhcwoduchowjg8w",
+    },
+)
+
+
+

The return value (odie) is an instance of pooch.Pooch. +It contains all of the information needed to fetch the data files in our +registry and store them in the specified cache folder.

+
+

Note

+

The Pooch registry is a mapping of file names and their associated +hashes (and optionally download URLs).

+
+
+

Tip

+

If you don’t know the hash or are otherwise unable to obtain it, it is +possible to bypass the check. This is not recommended for general use, +only if it can’t be avoided. See Hashes: Calculating and bypassing.

+
+
+

Attention

+

You can have data files in subdirectories of the remote data store +(URL). +These files will be saved to the same subdirectories in the local storage +folder.

+

However, the names of these files in the registry must use Unix-style +separators ('/') even on Windows. +Pooch will handle the appropriate conversions.

+
+
+
+

Downloading files

+

To download one our data files and load it with xarray:

+
import xarray as xr
+
+
+file_path = odie.fetch("gravity-disturbance.nc")
+# Standard use of xarray to load a netCDF file (.nc)
+data = xr.open_dataset(file_path)
+
+
+

The call to pooch.Pooch.fetch will check if the file already exists in +the cache folder.

+

If it doesn’t:

+
    +
  1. The file is downloaded and saved to the cache folder.

  2. +
  3. The hash of the downloaded file is compared against the one stored in the +registry to make sure the file isn’t corrupted.

  4. +
  5. The function returns the absolute path to the file on your computer.

  6. +
+

If it does:

+
    +
  1. Check if it’s hash matches the one in the registry.

  2. +
  3. If it does, no download happens and the file path is returned.

  4. +
  5. If it doesn’t, the file is downloaded once more to get an updated version on +your computer.

  6. +
+
+
+

Why use this method?

+

With pooch.Pooch, you can centralize the information about the URLs, +hashes, and files in a single place. +Once the instance is created, it can be used to fetch individual files without +repeating the URL and hash everywhere.

+

A good way to use this is to place the call to pooch.create in Python +module (a .py file). +Then you can import the module in .py scripts or Jupyter notebooks and +use the instance to fetch your data. +This way, you don’t need to define the URLs or hashes in multiple +scripts/notebooks.

+
+
+

Customizing the download

+

The pooch.Pooch.fetch method supports for all of Pooch’s +downloaders and processors. +You can use HTTP, FTP, and SFTP +(even with authentication), +decompress files, +unpack archives, +show progress bars, and more with a bit of configuration.

+
+
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/multiple-urls.html b/v1.8.0/multiple-urls.html new file mode 100644 index 00000000..31c70c1b --- /dev/null +++ b/v1.8.0/multiple-urls.html @@ -0,0 +1,587 @@ + + + + + + + + + + Multiple download URLs | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+ Contents +
+ +
+
+
+
+
+ +
+

Multiple download URLs

+ +
+
+ +
+

Contents

+
+ +
+
+
+ +
+ +
+

Multiple download URLs

+

You can set different download URLs for individual files with the urls +argument of pooch.create. +It should be a dictionary with the file names as keys and the URLs for +downloading the files as values.

+

For example, say we have a citadel.csv file that we want to download from +https://www.some-data-hosting-site.com instead:

+
# The basic setup is the same
+POOCH = pooch.create(
+    path=pooch.os_cache("plumbus"),
+    base_url="https://github.com/rick/plumbus/raw/{version}/data/",
+    version=version,
+    version_dev="main",
+    registry={
+        "c137.csv": "19uheidhlkjdwhoiwuhc0uhcwljchw9ochwochw89dcgw9dcgwc",
+        "cronen.csv": "1upodh2ioduhw9celdjhlfvhksgdwikdgcowjhcwoduchowjg8w",
+        # Still include the file in the registry
+        "citadel.csv": "893yprofwjndcwhx9c0ehp3ue9gcwoscjwdfgh923e0hwhcwiyc",
+    },
+    # Now specify custom URLs for some of the files in the registry.
+    urls={
+        "citadel.csv": "https://www.some-data-hosting-site.com/files/citadel.csv",
+    },
+)
+
+
+

When POOCH.fetch("citadel.csv") is called, the download will by from the +specified URL instead of the base_url. +The file name will not be appended automatically to the URL in case you want to +change the file name in local storage.

+
+

Attention

+

Versioning of custom URLs is not supported since they are assumed to be +data files independent of your project. +The file will still be placed in a versioned cache folder.

+
+
+

Tip

+

Custom URLs can be used along side base_url or you can omit +base_url entirely by setting it to an empty string (base_url=""). +Doing so requires setting a custom URL for every file in the registry.

+
+
+

Usage with registry files

+

You can also include custom URLs in a registry file by +adding the URL for a file to end of the line (separated by a space):

+
c137.csv 19uheidhlkjdwhoiwuhc0uhcwljchw9ochwochw89dcgw9dcgwc
+cronen.csv 1upodh2ioduhw9celdjhlfvhksgdwikdgcowjhcwoduchowjg8w
+citadel.csv 893yprofwjndcwhx9c0ehp3ue9gcwoscjwdfgh923e0hwhcwiyc https://www.some-data-hosting-site.com/files/citadel.csv
+
+
+

pooch.Pooch.load_registry will automatically populate the urls +attribute. +This way, custom URLs don’t need to be set in the code. +In fact, the module code doesn’t change at all:

+
# Define the Pooch exactly the same (urls is None by default)
+POOCH = pooch.create(
+    path=pooch.os_cache("plumbus"),
+    base_url="https://github.com/rick/plumbus/raw/{version}/data/",
+    version=version,
+    version_dev="main",
+    registry=None,
+)
+# If custom URLs are present in the registry file, they will be set
+# automatically.
+POOCH.load_registry(os.path.join(os.path.dirname(__file__), "registry.txt"))
+
+
+
+
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/objects.inv b/v1.8.0/objects.inv new file mode 100644 index 0000000000000000000000000000000000000000..1fa68b0058332e162a9d0297625c6ed4a497d7a2 GIT binary patch literal 1592 zcmV-82FLj$AX9K?X>NERX>N99Zgg*Qc_4OWa&u{KZXhxWBOp+6Z)#;@bUGkVZ*OC0 z3L_v^WpZ%ZEX>4U6X>%ZB zZ*6dLWpi_7WFU2OX>MmAdTeQ8E(&s4#`2A_PUoM$RfxB3D$_S z?a`nm+GbZ4RgrS*ocbDly*^3#Bk@m^sNLjd4QIX?jfUSx?zW7TCupy?XIAw!fu8}l z^et^F^7qY2oa>*Ay?6x$YL)NJ-NXGI`>UxLu1KdM%NlbIzv#dtX-J0!soo}>4d4vY z&2CrXy58+{F+`kL^&(7CvUA%JSy8Su>p73{jG|`IK78G*?^ocwh_t@6Ma++1pHp7w zy^OWKz-83UIH600X{Ti5&4Gwv!k21T05}q{>%01UASmDA3#K*R%PsaDII2UtjvOf$ z-Svk7?G7C~?6DCoPWA*w zc_QW0?m{|FS)(8heNzhcI}H-}Prz75;tg43N5$XA7O5lBc}+dqF)pOC-J66gQ15-nsIJXq*jvAALPXaz- zz#>auK5-`T0!}Nkgyd7323(Z)pM2FP3q8Dg2nDFjhw^7^egxzX9?H=#)3KErUKnX!r}G1&HWpcrLCa$ zRU8M21++X|*(Bi@LTp7@Qp{zTQVk>)P@WQVi4p_~sTDLUbh!*p4Z$;@Jj3RIx3f*! z=DTFJcP4`)1@H`LcE9KGMp6LJfU;+i9Paes0*M7Qf5ylra3>M)Eof27a;Tx^L1aYB z0+$1iq!3~&%Bo&2!&D+6v4ECTF_$7xPY7*jNi1@}fv7-eLvu2c1GQ2E?ipDbh@+i0 ztW$RiAB64f)#`KF0iG7olZ@|rC3D(d-*k+W|%q~dhTmM+nN+=XzgD=KB!wa zZH}O=pl+Ce!OlZ@!p)Hc8Z($o>F@P}9MZtk=Jb7KGS8a|mDGpTmI2kQ8t(O69jc_$b${R~Ra1v?k=EOt~XyT{Sz@D^-;9=UoC z6KNeld7#Sbb=0?D!MxU;Z%Rbk8m6(bh&Z|>a`u~&#^07UT*&*b3v-Sx>RJqZ&Q~;Y z+qW(2BSu<%V{g<%1t6Kcw_O+VO*FP@`d8bxv-TJ>!ae z{3x5=k5f0CWaODui3w@1#kgS7em4JwFtZXWi(hI&b8?DfL;9{xW2uFqios;}4LpAN z#4UtHm};QKP~RYp;%?D?72e%im$0$Vw^D$(C@cszr-K;BXDuH|7P1PgF#qn47CONi7cMv(>hjDbavuRcM^k z>8yt0H}vu#2^2B%);Ief@1+j^X0wdpk(ggb z%x!sQ;{MQ5B>t>kbd*wY(eU<-Dk!2 + + + + + + + Processors: Post-download actions | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+ Contents +
+ +
+
+
+
+
+ +
+

Processors: Post-download actions

+ +
+
+ +
+

Contents

+
+ +
+
+
+ +
+ +
+

Processors: Post-download actions

+

Post-download actions sometimes need to be taken on downloaded files +(unzipping, conversion to a more efficient format, etc). +If these actions are time or memory consuming, it might be worth doing them +only once after the file is downloaded. +This is a way of trading disk space for computation time. +pooch.Pooch.fetch and pooch.retrieve accept the processor +argument to handle these situations.

+

Processors are Python callable objects (like functions or classes with a +__call__ method) that are executed after a file is downloaded to perform +these actions. +They must have the following format:

+
def myprocessor(fname, action, pooch):
+    '''
+    Processes the downloaded file and returns a new file name.
+
+    The function **must** take as arguments (in order):
+
+    fname : str
+        The full path of the file in the local data storage
+    action : str
+        Either: "download" (file doesn't exist and will be downloaded),
+        "update" (file is outdated and will be downloaded), or "fetch"
+        (file exists and is updated so no download is necessary).
+    pooch : pooch.Pooch
+        The instance of the Pooch class that is calling this function.
+
+    The return value can be anything but is usually a full path to a file
+    (or list of files). This is what will be returned by Pooch.fetch and
+    pooch.retrieve in place of the original file path.
+    '''
+    ...
+    return full_path
+
+
+

The processor is executed after a file downloaded attempted (whether the +download actually happens or not) and before returning the path to the +downloaded file. +The processor lets us intercept the returned path, perform actions, and +possibly return a different path.

+

Pooch provides built-in processors for common tasks, like decompressing files +and unpacking tar and zip archives. See the List of functions and classes (API) for a full list.

+

Common uses cases for processors include:

+ +
+

Creating your own processors

+

Let’s say we want to implement the pooch.Unzip processor ourselves to +extract a single file from the archive. We could do that with the following +function:

+
import os
+from zipfile import ZipFile
+
+
+def unpack(fname, action, pup):
+    """
+    Post-processing hook to unzip a file and return the unzipped file name.
+
+    Parameters
+    ----------
+    fname : str
+       Full path of the zipped file in local storage
+    action : str
+       One of "download" (file doesn't exist and will download),
+       "update" (file is outdated and will download), and
+       "fetch" (file exists and is updated so no download).
+    pup : Pooch
+       The instance of Pooch that called the processor function.
+
+    Returns
+    -------
+    fname : str
+       The full path to the unzipped file. (Return the same fname is your
+       processor doesn't modify the file).
+
+    """
+    # Create a new name for the unzipped file. Appending something to the
+    # name is a relatively safe way of making sure there are no clashes
+    # with other files in the registry.
+    unzipped = fname + ".unzipped"
+    # Don't unzip if file already exists and is not being downloaded
+    if action in ("update", "download") or not os.path.exists(unzipped):
+        with ZipFile(fname, "r") as zip_file:
+            # Extract the data file from within the archive
+            with zip_file.open("actual-data-file.txt") as data_file:
+                # Save it to our desired file name
+                with open(unzipped, "wb") as output:
+                    output.write(data_file.read())
+    # Return the path of the unzipped file
+    return unzipped
+
+
+def fetch_zipped_file():
+    """
+    Load a large zipped sample data as a pandas.DataFrame.
+    """
+    # Pass in the processor to unzip the data file
+    fname = GOODBOY.fetch("zipped-data-file.zip", processor=unpack)
+    # fname is now the path of the unzipped file which can be loaded by
+    # pandas directly
+    data = pandas.read_csv(fname)
+    return data
+
+
+

Similarly, you could build any custom processor function so long as it receives +the fname, action, pup arguments. Example use cases for this would be:

+
    +
  • Converting data from a download-friendly format (compressed and minimal file +size) to a more user friendly format (easy to open and fast to load into +memory).

  • +
  • Add missing metadata to data from public servers. You might be using public +data that has known issues (poorly formated entries, missing metadata, etc) +which can be fixed when the file is downloaded.

  • +
+

The main advantage to using a processor for these actions is that they are +performed only when the file is downloaded. A modified version of the file can +be kept on disk so that loading the file is easier. This is particularly +convenient if the processor task takes a long time to run.

+
+
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/progressbars.html b/v1.8.0/progressbars.html new file mode 100644 index 00000000..888c9560 --- /dev/null +++ b/v1.8.0/progressbars.html @@ -0,0 +1,649 @@ + + + + + + + + + + Printing progress bars | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + + +
+
+
+
+ +
+

Printing progress bars

+ +
+ +
+
+ +
+ +
+

Printing progress bars

+
+

Using tqdm progress bars

+

Pooch uses tqdm to print a download progress +bar. This is turned off by default but can be enabled using +progressbar=True in pooch.retrieve:

+
fname = retrieve(
+    url="https://some-data-server.org/a-data-file.nc",
+    known_hash="md5:70e2afd3fd7e336ae478b1e740a5f08e",
+    progressbar=True,
+)
+
+
+

The resulting progress bar will be printed to the standard error stream +(STDERR) and should look something like this:

+
100%|█████████████████████████████████████████| 336/336 [...]
+
+
+

You can also do the same with pooch.Pooch.fetch:

+
POOCH = pooch.create(
+    ...
+)
+
+fname = POOCH.fetch(
+    "large-data-file.h5",
+    progressbar=True,
+)
+
+
+

Alternatively, you can pass progressbar=True directly into one of our +downloaders:

+
# Using fetch
+fname = POOCH.fetch(
+    "large-data-file.h5",
+    downloader=pooch.HTTPDownloader(progressbar=True),
+)
+
+# Using retrieve
+fname = retrieve(
+    url="https://some-data-server.org/a-data-file.nc",
+    known_hash="md5:70e2afd3fd7e336ae478b1e740a5f08e",
+    downloader=pooch.HTTPDownloader(progressbar=True),
+)
+
+
+
+

Note

+

tqdm is not installed by default with Pooch. You will have to install +it separately in order to use this feature.

+
+
+
+

Using custom progress bars

+
+

Note

+

At the moment, this feature is only available for +pooch.HTTPDownloader.

+
+

Alternatively, you can pass an arbitrary object that behaves like a progress +that implements the update, reset, and close methods:

+
    +
  • update should accept a single integer positional argument representing +the current completion (in bytes).

  • +
  • reset and close do not take any argument beside self.

  • +
+

The object must also have a total attribute that can be set from outside +the class. +In other words, the custom progress bar needs to behave like a tqdm +progress bar.

+

Here’s a minimal working example of such a custom “progress display” class:

+
import sys
+
+class MinimalProgressDisplay:
+    def __init__(self, total):
+        self.count = 0
+        self.total = total
+
+    def __repr__(self):
+        return str(self.count) + "/" + str(self.total)
+
+    def render(self):
+        print(f"\r{self}", file=sys.stderr, end="")
+
+    def update(self, i):
+        self.count = i
+        self.render()
+
+    def reset(self):
+        self.count = 0
+
+    def close(self):
+        print("", file=sys.stderr)
+
+
+

An instance of this class can now be passed to an HTTPDownloader as:

+
# Assuming you have a pooch.Pooch instance setup
+POOCH = pooch.create(
+    ...
+)
+
+minimal_progress = MinimalProgressDisplay(total=None)
+
+fname = POOCH.fetch(
+    "large-data-file.h5",
+    downloader=pooch.HTTPDownloader(progressbar=minimal_progress),
+)
+
+
+
+
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/protocols.html b/v1.8.0/protocols.html new file mode 100644 index 00000000..e64a2486 --- /dev/null +++ b/v1.8.0/protocols.html @@ -0,0 +1,626 @@ + + + + + + + + + + Download protocols | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+ Contents +
+ +
+
+
+
+
+ +
+

Download protocols

+ +
+
+ +
+

Contents

+
+ +
+
+
+ +
+ +
+

Download protocols

+

Pooch supports the HTTP, FTP, and SFTP protocols by default. +It also includes a custom protocol for Digital Object Identifiers (DOI) from +providers like figshare and Zenodo (see below). +It will automatically detect the correct protocol from the URL and use the +appropriate download method.

+
+

Note

+

To download files over SFTP, +paramiko needs to be installed.

+
+

For example, if our data were hosted on an FTP server, we could use the +following setup:

+
POOCH = pooch.create(
+    path=pooch.os_cache("plumbus"),
+    # Use an FTP server instead of HTTP. The rest is all the same.
+    base_url="ftp://garage-basement.org/{version}/",
+    version=version,
+    version_dev="main",
+    registry={
+        "c137.csv": "19uheidhlkjdwhoiwuhc0uhcwljchw9ochwochw89dcgw9dcgwc",
+        "cronen.csv": "1upodh2ioduhw9celdjhlfvhksgdwikdgcowjhcwoduchowjg8w",
+    },
+)
+
+
+def fetch_c137():
+    """
+    Load the C-137 sample data as a pandas.DataFrame (over FTP this time).
+    """
+    fname = POOCH.fetch("c137.csv")
+    data = pandas.read_csv(fname)
+    return data
+
+
+

You can even specify custom functions for the download or login credentials for +authentication. See Downloaders: Customizing the download for more information.

+
+

Digital Object Identifiers (DOIs)

+

Pooch can download files stored in data repositories from the DOI by formatting +the URL as doi:{DOI}/{file name}. +Notice that there are no // like in HTTP/FTP and you must specify a file +name after the DOI (separated by a /).

+
+

See also

+

For a list of supported data repositories, see +pooch.DOIDownloader.

+
+

For example, one of our test files ("tiny-data.txt") is stored in the +figshare dataset +doi:10.6084/m9.figshare.14763051.v1. +We can could use pooch.retrieve to download it like so:

+
file_path = pooch.retrieve(
+    url="doi:10.6084/m9.figshare.14763051.v1/tiny-data.txt",
+    known_hash="md5:70e2afd3fd7e336ae478b1e740a5f08e",
+)
+
+
+

We can also make a pooch.Pooch with a registry stored entirely on a +figshare dataset:

+
POOCH = pooch.create(
+    path=pooch.os_cache("plumbus"),
+    # Use the figshare DOI
+    base_url="doi:10.6084/m9.figshare.14763051.v1/",
+    registry={
+        "tiny-data.txt": "md5:70e2afd3fd7e336ae478b1e740a5f08e",
+        "store.zip": "md5:7008231125631739b64720d1526619ae",
+    },
+)
+
+
+def fetch_tiny_data():
+    """
+    Load the tiny data as a numpy array.
+    """
+    fname = POOCH.fetch("tiny-data.txt")
+    data = numpy.loadtxt(fname)
+    return data
+
+
+
+

Warning

+

A figshare DOI must point to a figshare dataset, not a figshare +collection. Collection DOIs have a .c. in them, e.g. +doi:10.6084/m9.figshare.c.4362224.v1. Attempting to download files +from a figshare collection will raise an error. +See issue #274 details.

+
+

Since this type of repositories store information about the files contained in +them, we can avoid having to manually type the registry with the file names and +their hashes. +Instead, we can use the pooch.Pooch.load_registry_from_doi to +automatically populate the registry:

+
POOCH = pooch.create(
+    path=pooch.os_cache("plumbus"),
+    # Use the figshare DOI
+    base_url="doi:10.6084/m9.figshare.14763051.v1/",
+    registry=None,
+)
+
+# Automatically populate the registry
+POOCH.load_registry_from_doi()
+
+# Fetch one of the files in the repository
+fname = POOCH.fetch("tiny-data.txt")
+
+
+
+
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/py-modindex.html b/v1.8.0/py-modindex.html new file mode 100644 index 00000000..0c52be54 --- /dev/null +++ b/v1.8.0/py-modindex.html @@ -0,0 +1,479 @@ + + + + + + + + + + Python Module Index | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

+ +
+
+ +
+
+
+ +
+ + +

Python Module Index

+ +
+ p +
+ + + + + + + +
 
+ p
+ pooch +
+ + +
+ + + +
+
+ +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/registry-files.html b/v1.8.0/registry-files.html new file mode 100644 index 00000000..6831c087 --- /dev/null +++ b/v1.8.0/registry-files.html @@ -0,0 +1,710 @@ + + + + + + + + + + Registry files | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + + +
+
+
+
+ + + +
+ +
+

Registry files

+
+

Usage

+

If your project has a large number of data files, it can be tedious to list +them in a dictionary. In these cases, it’s better to store the file names and +hashes in a file and use pooch.Pooch.load_registry to read them.

+
import os
+import pkg_resources
+
+POOCH = pooch.create(
+    path=pooch.os_cache("plumbus"),
+    base_url="https://github.com/rick/plumbus/raw/{version}/data/",
+    version=version,
+    version_dev="main",
+    # We'll load it from a file later
+    registry=None,
+)
+# Get registry file from package_data
+registry_file = pkg_resources.resource_stream("plumbus", "registry.txt")
+# Load this registry file
+POOCH.load_registry(registry_file)
+
+
+

In this case, the registry.txt file is in the plumbus/ package +directory and should be shipped with the package (see below for instructions). +We use pkg_resources +to access the registry.txt, giving it the name of our Python package.

+
+
+

Registry file format

+

Registry files are light-weight text files that specify a file’s name and hash. +In our example, the contents of registry.txt are:

+
c137.csv 19uheidhlkjdwhoiwuhc0uhcwljchw9ochwochw89dcgw9dcgwc
+cronen.csv 1upodh2ioduhw9celdjhlfvhksgdwikdgcowjhcwoduchowjg8w
+
+
+

A specific hashing algorithm can be enforced, if a checksum for a file is +prefixed with alg::

+
c137.csv sha1:e32b18dab23935bc091c353b308f724f18edcb5e
+cronen.csv md5:b53c08d3570b82665784cedde591a8b0
+
+
+

From Pooch v1.2.0 the registry file can also contain line comments, prepended +with a #:

+
# C-137 sample data
+c137.csv 19uheidhlkjdwhoiwuhc0uhcwljchw9ochwochw89dcgw9dcgwc
+# Cronenberg sample data
+cronen.csv 1upodh2ioduhw9celdjhlfvhksgdwikdgcowjhcwoduchowjg8w
+
+
+
+

Attention

+

Make sure you set the Pooch version in your setup.py to >=1.2.0 when +using comments as earlier versions cannot handle them: +install_requires = [..., "pooch>=1.2.0", ...]

+
+
+
+

Packaging registry files

+

To make sure the registry file is shipped with your package, include the +following in your MANIFEST.in file:

+
include plumbus/registry.txt
+
+
+

And the following entry in the setup function of your setup.py file:

+
setup(
+    ...
+    package_data={"plumbus": ["registry.txt"]},
+    ...
+)
+
+
+
+
+

Creating a registry file

+

If you have many data files, creating the registry and keeping it updated can +be a challenge. Function pooch.make_registry will create a registry +file with all contents of a directory. For example, we can generate the +registry file for our fictitious project from the command-line:

+
$ python -c "import pooch; pooch.make_registry('data', 'plumbus/registry.txt')"
+
+
+
+
+

Create registry file from remote files

+

If you want to create a registry file for a large number of data files that are +available for download but you don’t have their hashes or any local copies, +you must download them first. Manually downloading each file +can be tedious. However, we can automate the process using +pooch.retrieve. Below, we’ll explore two different scenarios.

+

If the data files share the same base url, we can use pooch.retrieve +to download them and then use pooch.make_registry to create the +registry:

+
import os
+
+# Names of the data files
+filenames = ["c137.csv", "cronen.csv", "citadel.csv"]
+
+# Base url from which the data files can be downloaded from
+base_url = "https://www.some-data-hosting-site.com/files/"
+
+# Create a new directory where all files will be downloaded
+directory = "data_files"
+os.makedirs(directory)
+
+# Download each data file to data_files
+for fname in filenames:
+    path = pooch.retrieve(
+        url=base_url + fname, known_hash=None, fname=fname, path=directory
+    )
+
+# Create the registry file from the downloaded data files
+pooch.make_registry("data_files", "registry.txt")
+
+
+

If each data file has its own url, the registry file can be manually created +after downloading each data file through pooch.retrieve:

+
import os
+
+# Names and urls of the data files. The file names are used for naming the
+# downloaded files. These are the names that will be included in the registry.
+fnames_and_urls = {
+    "c137.csv": "https://www.some-data-hosting-site.com/c137/data.csv",
+    "cronen.csv": "https://www.some-data-hosting-site.com/cronen/data.csv",
+    "citadel.csv": "https://www.some-data-hosting-site.com/citadel/data.csv",
+}
+
+# Create a new directory where all files will be downloaded
+directory = "data_files"
+os.makedirs(directory)
+
+# Create a new registry file
+with open("registry.txt", "w") as registry:
+    for fname, url in fnames_and_urls.items():
+        # Download each data file to the specified directory
+        path = pooch.retrieve(
+            url=url, known_hash=None, fname=fname, path=directory
+        )
+        # Add the name, hash, and url of the file to the new registry file
+        registry.write(
+            f"{fname} {pooch.file_hash(path)} {url}\n"
+        )
+
+
+
+

Warning

+

Notice that there are no checks for download integrity (since we don’t +know the file hashes before hand). Only do this for trusted data sources +and over a secure connection. If you have access to file hashes/checksums, +we highly recommend using them to set the known_hash argument.

+
+
+
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/retrieve.html b/v1.8.0/retrieve.html new file mode 100644 index 00000000..4ed3e80a --- /dev/null +++ b/v1.8.0/retrieve.html @@ -0,0 +1,645 @@ + + + + + + + + + + Retrieving a single data file | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + + +
+
+
+
+ +
+

Retrieving a single data file

+ +
+ +
+
+ +
+ +
+

Retrieving a single data file

+
+

Basic usage

+

If you only want to download one or two data files, use the +pooch.retrieve function:

+
import pooch
+
+
+file_path = pooch.retrieve(
+    # URL to one of Pooch's test files
+    url="https://github.com/fatiando/pooch/raw/v1.0.0/data/tiny-data.txt",
+    known_hash="md5:70e2afd3fd7e336ae478b1e740a5f08e",
+)
+
+
+

The code above will:

+
    +
  1. Check if the file from this URL already exists in Pooch’s default cache +folder (see pooch.os_cache).

  2. +
  3. If it doesn’t, the file is downloaded and saved to the cache folder.

  4. +
  5. The MD5 hash +is compared against the known_hash to make sure the file isn’t +corrupted.

  6. +
  7. The function returns the absolute path to the file on your computer.

  8. +
+

If the file already existed on your machine, Pooch will check if it’s MD5 hash +matches the known_hash:

+
    +
  • If it does, no download happens and the file path is returned.

  • +
  • If it doesn’t, the file is downloaded once more to get an updated version on +your computer.

  • +
+

Since the download happens only once, you can place this function call at the +start of your script or Jupyter notebook without having to worry about repeat +downloads. +Anyone getting a copy of your code should also get the correct data file the +first time they run it.

+
+

See also

+

Pooch can handle multiple download protocols like HTTP, FTP, SFTP, and +even download from repositories like figshare +and Zenodo by using the DOI instead of a URL. +See Download protocols.

+
+
+

See also

+

You can use different hashes by specifying different algorithm names: +sha256:XXXXXX, sha1:XXXXXX, etc. See Hashes: Calculating and bypassing.

+
+
+
+

Unknown file hash

+

If you don’t know the hash of the file, you can set known_hash=None to +bypass the check. +retrieve will print a log message with the SHA256 hash of the +downloaded file. +It’s highly recommended that you copy and paste this hash into your code +and use it as the known_hash.

+
+

Tip

+

Setting the known_hash guarantees that the next time your code is run +(by you or someone else) the exact same file is downloaded. This helps +make the results of your code reproducible.

+
+
+
+

Customizing the download

+

The pooch.retrieve function supports for all of Pooch’s +downloaders and processors. +You can use HTTP, FTP, and SFTP +(even with authentication), +decompress files, +unpack archives, +show progress bars, and more with a bit of configuration.

+
+
+

When not to use retrieve

+

If you need to manage the download and caching of several files from one or +more sources, then you should start using the full capabilities of the +pooch.Pooch class. +It can handle sandboxing +data for different package versions, allow users to set the download +locations, and more.

+

The classic example is a Python package that contains several sample +datasets for use in testing and documentation.

+

See Fetching files from a registry and Manage a package’s sample data to get started.

+
+
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/sample-data.html b/v1.8.0/sample-data.html new file mode 100644 index 00000000..f4186f75 --- /dev/null +++ b/v1.8.0/sample-data.html @@ -0,0 +1,756 @@ + + + + + + + + + + Manage a package’s sample data | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + + +
+
+
+
+ +
+

Manage a package’s sample data

+ +
+ +
+
+ +
+ +
+

Manage a package’s sample data

+

In this section, we’ll use Pooch to manage the download of a Python package’s +sample datasets.

+
+

Note

+

The setup will be very similar to what we saw in Fetching files from a registry. +It may be helpful to read that first.

+
+
+

The problem

+

In this example, we’ll work with the follow assumptions:

+
    +
  • You develop a Python library called plumbus for analysing data emitted by +interdimensional portals.

  • +
  • You want to distribute sample data so that your users can easily try out the +library by copying and pasting from the documentation.

  • +
  • You want to have a plumbus.datasets module that defines functions like +fetch_c137() that will return the data loaded as a +pandas.DataFrame for convenient access.

  • +
  • Your sample data are in a folder of your GitHub repository but you don’t want +to include the data files with your source and wheel distributions because of +their size.

  • +
  • You use git tags to mark releases of your project.

  • +
  • Your project has a variable that defines the version string.

  • +
  • The version string contains an indicator that the current commit is not a +release (like 'v1.2.3+12.d908jdl' or 'v0.1+dev').

  • +
+

For now, let’s say that this is the layout of your repository on GitHub:

+
doc/
+    ...
+data/
+    README.md
+    c137.csv
+    cronen.csv
+plumbus/
+    __init__.py
+    ...
+    datasets.py
+setup.py
+...
+
+
+

The sample data are stored in the data folder of your repository.

+
+

See also

+

Pooch can handle different use cases as well, like: FTP/SFTP, authenticated +HTTP, multiple URLs, decompressing and unpacking archives, etc. +See the tutorials under “Training your Pooch” and the documentation for +pooch.create and pooch.Pooch for more options.

+
+
+
+

Basic setup

+

This is what the plumbus/datasets.py file would look like:

+
"""
+Load sample data.
+"""
+import pandas
+import pooch
+
+from . import version  # The version string of your project
+
+
+BRIAN = pooch.create(
+    # Use the default cache folder for the operating system
+    path=pooch.os_cache("plumbus"),
+    # The remote data is on Github
+    base_url="https://github.com/rick/plumbus/raw/{version}/data/",
+    version=version,
+    # If this is a development version, get the data from the "main" branch
+    version_dev="main",
+    registry={
+        "c137.csv": "sha256:19uheidhlkjdwhoiwuhc0uhcwljchw9ochwochw89dcgw9dcgwc",
+        "cronen.csv": "sha256:1upodh2ioduhw9celdjhlfvhksgdwikdgcowjhcwoduchowjg8w",
+    },
+)
+
+
+def fetch_c137():
+    """
+    Load the C-137 sample data as a pandas.DataFrame.
+    """
+    # The file will be downloaded automatically the first time this is run
+    # returns the file path to the downloaded file. Afterwards, Pooch finds
+    # it in the local cache and doesn't repeat the download.
+    fname = BRIAN.fetch("c137.csv")
+    # The "fetch" method returns the full path to the downloaded data file.
+    # All we need to do now is load it with our standard Python tools.
+    data = pandas.read_csv(fname)
+    return data
+
+
+def fetch_cronen():
+    """
+    Load the Cronenberg sample data as a pandas.DataFrame.
+    """
+    fname = BRIAN.fetch("cronen.csv")
+    data = pandas.read_csv(fname)
+    return data
+
+
+

The BRIAN variable captures the value returned by pooch.create, +which is an instance of the Pooch class. The class contains the +data registry (files, URLs, hashes, etc) and handles downloading files from the +registry using the fetch method. +When the user calls plumbus.datasets.fetch_c137() for the first time, the +data file will be downloaded and stored in the local storage.

+
+

Tip

+

We’re using pooch.os_cache to set the local folder to the default +cache location for the user’s operating system. You could also provide any +other path if you prefer.

+
+
+
+

Versioning

+

The files from different version of your project will be kept in separate +folders to make sure they don’t conflict with each other. This way, you can +safely update data files while maintaining backward compatibility. For example, +if path=".plumbus" and version="v0.1", the data folder will be +.plumbus/v0.1.

+

When your project updates, Pooch will automatically setup a separate folder for +the new data files based on the given version string. The remote URL will also +be updated. Notice that there is a format specifier {version} in the URL +that Pooch substitutes for you.

+

Versioning is optional and can be ignored by omitting the version and +version_dev arguments or setting them to None.

+
+
+

Retry failed downloads

+

When downloading data repeatedly, like in continuous integration, failures can +occur due to sporadic network outages or other factors outside of our control. +In these cases, it can be frustrating to have entire jobs fail because a single +download was not successful.

+

Pooch allows you to specify a number of times to retry the download in case of +failure by setting retry_if_failed in pooch.create. This setting +will be valid for all downloads attempted with pooch.Pooch.fetch. The +download can fail because the file hash doesn’t match the known hash (due to a +partial download, for example) or because of network errors coming from +requests. Other errors (file system permission errors, etc) will still +result in a failed download.

+
+

Note

+

Requires Pooch >= 1.3.0.

+
+
+
+

Disable file updates for testing

+

Sometimes we can forget to update the hash of a file in the registry when we +change one of the existing data files. +If this happens in a pull request or any branch that is not the default, Pooch +will detect that there is a mismatch and will update the local file by +re-downloading (usually from the default development branch). +If your tests don’t check the file contents exactly (which is usually not +practical), you can have tests that pass on development or continuous +integration and then fail once a pull request is merged.

+

In these cases, it is better to temporarily disallow file updates so that Pooch +raises an error when the hash doesn’t match (indicating that you forgot to +update it). +To do so, use the allow_updates argument in pooch.create. +Setting this to False will mean that a hash mismatch between local file and +the registry always results in an error.

+
+

Tip

+

We do not recommend setting this permanenetly to False. Instead, +set it to the name of an environment variable that activates this +behaviour, like pooch.create(..., +allow_updates="MYPROJECT_ALLOW_UPDATES"). +Then you can set MYPROJECT_ALLOW_UPDATES=false on continuous +integration or when running your tests locally.

+
+
+

Note

+

Requires Pooch >= 1.6.0.

+
+
+
+

Where to go from here

+

Pooch has more features for handling different download protocols, handling +large registries, downloading from multiple sources, and more. Check out the +tutorials under “Training your Pooch” for more information.

+

Most users will also benefit from reading at least:

+ +
+
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/search.html b/v1.8.0/search.html new file mode 100644 index 00000000..02ed40a9 --- /dev/null +++ b/v1.8.0/search.html @@ -0,0 +1,490 @@ + + + + + + + + + + Search | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

+ +
+
+ +
+
+
+ +
+ +

Search

+ + + + +

+ Searching for multiple words only shows matches that contain + all words. +

+ + +
+ + + +
+ + + +
+ +
+ + +
+ + + +
+
+ +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/searchindex.js b/v1.8.0/searchindex.js new file mode 100644 index 00000000..b0999cb0 --- /dev/null +++ b/v1.8.0/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({docnames:["about","api/generated/pooch.DOIDownloader","api/generated/pooch.Decompress","api/generated/pooch.FTPDownloader","api/generated/pooch.HTTPDownloader","api/generated/pooch.Pooch","api/generated/pooch.SFTPDownloader","api/generated/pooch.Untar","api/generated/pooch.Unzip","api/generated/pooch.check_version","api/generated/pooch.create","api/generated/pooch.file_hash","api/generated/pooch.get_logger","api/generated/pooch.make_registry","api/generated/pooch.os_cache","api/generated/pooch.retrieve","api/generated/pooch.test","api/index","authentication","changes","citing","compatibility","decompressing","downloaders","hashes","index","install","logging","multiple-files","multiple-urls","processors","progressbars","protocols","registry-files","retrieve","sample-data","unpacking","user-defined-cache","versions"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":4,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":3,"sphinx.domains.rst":2,"sphinx.domains.std":2,"sphinx.ext.intersphinx":1,"sphinx.ext.viewcode":1,sphinx:56},filenames:["about.rst","api/generated/pooch.DOIDownloader.rst","api/generated/pooch.Decompress.rst","api/generated/pooch.FTPDownloader.rst","api/generated/pooch.HTTPDownloader.rst","api/generated/pooch.Pooch.rst","api/generated/pooch.SFTPDownloader.rst","api/generated/pooch.Untar.rst","api/generated/pooch.Unzip.rst","api/generated/pooch.check_version.rst","api/generated/pooch.create.rst","api/generated/pooch.file_hash.rst","api/generated/pooch.get_logger.rst","api/generated/pooch.make_registry.rst","api/generated/pooch.os_cache.rst","api/generated/pooch.retrieve.rst","api/generated/pooch.test.rst","api/index.rst","authentication.rst","changes.rst","citing.rst","compatibility.rst","decompressing.rst","downloaders.rst","hashes.rst","index.rst","install.rst","logging.rst","multiple-files.rst","multiple-urls.rst","processors.rst","progressbars.rst","protocols.rst","registry-files.rst","retrieve.rst","sample-data.rst","unpacking.rst","user-defined-cache.rst","versions.rst"],objects:{"":[[17,0,0,"-","pooch"]],"pooch.DOIDownloader":[[1,2,1,"","__call__"]],"pooch.Decompress":[[2,2,1,"","__call__"]],"pooch.FTPDownloader":[[3,2,1,"","__call__"]],"pooch.HTTPDownloader":[[4,2,1,"","__call__"]],"pooch.Pooch":[[5,2,1,"","fetch"],[5,2,1,"","get_url"],[5,2,1,"","is_available"],[5,2,1,"","load_registry"],[5,2,1,"","load_registry_from_doi"]],"pooch.SFTPDownloader":[[6,2,1,"","__call__"]],"pooch.Untar":[[7,2,1,"","__call__"]],"pooch.Unzip":[[8,2,1,"","__call__"]],pooch:[[1,1,1,"","DOIDownloader"],[2,1,1,"","Decompress"],[3,1,1,"","FTPDownloader"],[4,1,1,"","HTTPDownloader"],[5,1,1,"","Pooch"],[6,1,1,"","SFTPDownloader"],[7,1,1,"","Untar"],[8,1,1,"","Unzip"],[9,3,1,"","check_version"],[10,3,1,"","create"],[11,3,1,"","file_hash"],[12,3,1,"","get_logger"],[13,3,1,"","make_registry"],[14,3,1,"","os_cache"],[15,3,1,"","retrieve"],[16,3,1,"","test"]]},objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","method","Python method"],"3":["py","function","Python function"]},objtypes:{"0":"py:module","1":"py:class","2":"py:method","3":"py:function"},terms:{"0":[5,9,10,15,21,31,33,34,35,36,38],"01":19,"01943":20,"02":19,"03":19,"04":19,"05":19,"06":19,"08":19,"09":19,"092odwhi2ujdp2du2od2odh2wod2":15,"0fc74468e6a9a829f103d069aeb2bb4f8646bad58bf146bb0e3379b759ec4a00":11,"0hljc7298ndo2":24,"1":[1,4,9,10,15,21,33,35,38],"10":[1,5,9,10,19,20,32],"100":[19,31],"10037888":19,"1024":[1,3,4],"107":19,"108":19,"11":19,"111":9,"113":19,"115":19,"116":19,"117":19,"118":19,"12":[10,35],"120":19,"123":19,"127":19,"128":19,"129":19,"13":19,"132":19,"133":19,"134":19,"137":[18,32,33,35],"138":19,"14":19,"140":19,"144":19,"14763051":[1,32],"149":19,"15":19,"151":19,"152":19,"156":19,"157":19,"159":19,"161":19,"165":19,"166":19,"168":19,"17":19,"171":19,"173":19,"179":19,"180":19,"189":19,"19":19,"191":19,"192":19,"194":19,"1943":20,"196":19,"198":19,"19uheidhlkjdwhoiwuhc0uhcwljchw9ochwochw89dcgw9dcgwc":[28,29,32,33,35,37],"1a10":9,"1s":[5,10,19],"1upodh2ioduhw9celdjhlfvhksgdwikdgcowjhcwoduchowjg8w":[28,29,32,33,35,37],"2":[1,4,15,21,33,35,38],"20":19,"200":19,"201":19,"2018":[0,19],"2019":19,"202":19,"2020":[19,20],"2021":19,"2022":19,"2023":19,"203":19,"204":19,"21":[3,19],"21105":20,"213":19,"214":19,"215":19,"216":19,"218":19,"22":[6,19],"220":19,"221":19,"224":19,"228":19,"23":19,"231":19,"235":19,"236":19,"237":19,"238":19,"24":[19,21],"241":19,"242":19,"243":19,"244":19,"25":19,"254":19,"255":19,"257":19,"259":19,"260":19,"265":19,"266":19,"267":19,"268":19,"27":19,"274":32,"275":19,"277":19,"278":19,"279":19,"28":19,"280":19,"281":19,"282":19,"286":19,"287":19,"288":19,"289":19,"29":19,"291":19,"292":19,"293":19,"295":19,"299":19,"3":[1,4,15,21,26,35,38],"30":19,"300":19,"305":19,"308":19,"31":19,"311":19,"312":19,"315":19,"316":19,"318":19,"32":19,"320":19,"321":19,"322":19,"323":19,"325":19,"328":19,"33":19,"333":19,"335":19,"336":31,"339":19,"340":19,"343":19,"344":19,"345":19,"348":19,"349":19,"353":19,"359":19,"361":19,"37":19,"370":19,"375":19,"4":[1,4,15,38],"41":19,"42":19,"4362224":32,"45":[19,20],"49":19,"4914758":19,"4924875":1,"5":[1,4,15,20,21,38],"50":19,"52":19,"5235242":19,"5242882":19,"5281":[1,19],"55":19,"5560923":19,"56":19,"57":19,"5793074":19,"59":19,"6":[1,4,15,21,35,38],"6084":[1,32],"64":19,"66":19,"69":19,"6a71973c93eac6c8839ce751ce10ae48":24,"7":[21,26,38],"7008231125631739b64720d1526619ae":32,"70e2afd3fd7e336ae478b1e740a5f08":[15,31,32,34],"72":19,"7678844":19,"77":19,"78":19,"79":19,"8":38,"80":19,"803o3uh2pecb2p3829d1bwouh9d":24,"81":19,"81whdo2d2e928yd1wi22":15,"84":19,"85":19,"88":19,"8812ba10b6c7778014fdae81b03f9def":15,"893yprofwjndcwhx9c0ehp3ue9gcwoscjwdfgh923e0hwhcwiyc":29,"8dl8dh9":9,"9":19,"9081wo2eb2gc0u":10,"93":19,"97":19,"98":19,"9hdg36":9,"bj\u00f6rn":19,"boolean":[3,4,23],"break":[19,21],"byte":[1,3,4,31],"case":[5,18,19,23,24,29,30,33,35,36,37],"char":19,"cl\u00e9ment":19,"class":[1,2,3,4,5,6,7,8,18,19,23,25,27,30,31,34,35],"default":[2,5,7,8,10,12,14,15,18,19,22,23,24,28,29,31,32,34,35,36,37],"do":[5,15,19,29,30,31,33,35,36],"function":[0,5,7,8,10,14,15,19,23,24,25,28,30,32,33,34,35],"import":[1,4,10,11,15,17,18,19,22,23,24,28,30,31,33,34,35,36],"int":[1,3,4,5,6,10],"long":[2,30],"new":[1,2,5,19,22,23,24,25,27,30,33,35,36],"public":[1,19,20,30],"r\u00e9mi":19,"return":[2,3,4,5,7,8,9,10,11,12,14,15,16,18,19,22,23,28,30,31,32,34,35,36],"rub\u00e9n":19,"short":21,"switch":10,"true":[1,3,4,5,6,10,13,15,16,23,31],"try":[4,10,21,25,35],"while":[19,35],A:[1,4,5,7,8,9,10,15,19,20,25,28,30,32,33],And:33,As:[19,23],At:31,By:[5,10,15,23,24,36],For:[2,9,15,18,22,26,29,32,33,35,36],If:[1,3,4,5,6,7,8,9,10,13,15,16,19,20,21,22,23,24,28,29,30,33,34,35,36],In:[5,18,19,21,23,24,28,29,31,33,35,37],It:[0,1,5,15,18,19,28,29,32,34,35],NOT:15,No:[5,19,23],Not:[1,4,5,10],On:[0,19,26],One:[18,30],Or:24,The:[1,2,3,4,5,6,7,8,10,11,12,14,15,18,19,20,21,22,23,24,26,27,28,29,30,31,32,33,34,37],Then:[28,35],There:[4,15,26,37],These:[19,28,33],To:[2,10,15,18,19,23,24,28,32,33,35,36,37],Will:[3,4,15,23],With:28,__:11,__call__:[1,2,3,4,6,7,8,23,30],__file__:[19,29],__init__:[31,35],__repr__:31,__version__:[4,15,19],abernathei:19,abl:1,about:[19,27,28,32,34],abov:[2,23,26,34],absolut:[5,15,28,34],accept:[23,30,31],access:[1,17,19,20,33,35,38],account:[3,6,19],across:19,action:[2,5,7,8,15,19,25],activ:35,actual:[1,30,36],ad:[1,5,7,8,10,19,29],adapt:21,add:[5,19,25,30,33],adjust:12,advantag:30,after:[1,5,15,22,23,30,32,33],afterward:35,again:[10,15,19,24],against:[15,28,34],agustina:19,ahead:[9,10],alessia:19,aletheia:19,alex:19,alexanderjuestel:19,alg:[5,11,19,24,33],algorithm:[5,11,15,19,26,33,34],algorithsm:19,all:[1,4,5,7,8,10,13,17,19,21,28,29,32,33,34,35,36],allow:[1,4,5,10,19,21,34,35,37],allow_upd:[5,10,35],along:[5,29],alpha:19,alreadi:[0,2,15,19,22,28,30,34],also:[0,3,5,18,19,24,29,31,32,33,34,35],altern:[1,4,5,15,31],alwai:[19,35],ampl:21,an:[1,3,4,5,6,10,15,19,20,22,23,24,27,28,29,31,32,34,35,36,37],anaconda:26,analys:35,analysi:16,analyt:19,anderson:[19,20],ani:[5,10,21,23,30,31,33,35],anirudh:19,announc:19,anon2:24,anonym:[3,6,18,19],anoth:0,antonio:19,anyon:34,anyth:30,api:[1,5,19,21,23,25,30],app:26,appauthor:14,appdata:14,appdir:19,append:[10,15,29,30,36,37],appnam:14,appropri:[10,19,23,24,28,32],ar:[1,2,3,4,5,9,10,14,15,17,18,19,21,23,24,26,27,28,29,30,32,33,35],arbitrari:[1,4,5,6,15,31],archiv:[1,2,7,8,12,15,19,22,25,27,28,30,34,35],aren:19,argument:[1,2,4,5,10,15,19,23,24,29,30,31,33,35],around:[17,18,23,24],arrai:32,articl:[19,20],artifact:19,ask:[4,18,25],assertionerror:16,associ:[20,28],assum:[10,15,24,28,29,31],assumpt:35,attempt:[5,10,19,30,32,35],attribut:[2,29,31],au:24,australia:24,auth:[4,18,23],authent:[0,3,4,5,6,19,23,25,28,32,34,35],author:[19,20,25],auto:2,autom:[19,33],automat:[2,10,15,19,26,29,32,35],avail:[3,4,5,19,24,31,33],avoid:[5,15,17,19,28,32,37],b53c08d3570b82665784cedde591a8b0:33,back:19,backport:19,backward:[19,35],bad:[5,10],baee0894dba14b12085eacb204284b97e362f4f3e5a5807693cc90ef415c1b2d:24,banihirw:[19,20],bar:[1,3,4,5,6,15,19,23,25,26,28,34],base:[5,10,14,19,28,33,35],base_url:[5,10,19,24,28,29,32,33,35,37],basement:32,basic:[0,4,18,29],becaus:[5,10,24,35],been:[5,11,15,22],befor:[5,15,19,21,22,30,33],behav:31,behaviour:[22,35],being:[30,37],below:[15,21,32,33,38],benefit:35,besid:31,best:[0,5],better:[19,33,35],between:[0,5,10,19,35],beyond:24,bibtex:[19,20],binari:[19,22],bit:[28,34],blank:19,boi:5,bom:24,bom_daily_st:24,bonu:0,book:19,bool:[1,3,4,5,6,10,13,15,16,23],born:0,both:[0,18,19,36],bound:19,box:19,branch:[35,38],brian:[19,35,37],broken:19,browser:19,bucklei:19,bug:19,build:[19,20,30],built:[0,30],bypass:[15,25,28,34,35],bz2:2,bz2file:19,bzip2:[2,15,19],c137:[18,24,29,32,33,35,37],c:[14,18,32,33,35],cach:[0,7,8,10,14,15,19,24,25,27,28,29,34,35,36],cache_path:14,calcul:[11,25,28,34,35],call:[0,1,2,3,4,5,6,7,8,10,15,19,23,24,28,29,30,34,35,37],callabl:[5,15,23,30],can:[1,2,4,5,10,15,18,19,20,22,23,24,27,28,29,30,31,32,33,34,35,36,37],cannot:[6,33],capabl:[0,34],capit:19,captur:[19,21,35],care:27,caus:[2,4,19,21,22],caution:[2,22],central:28,cff:19,challeng:33,chang:[1,2,3,4,9,11,19,22,24,27,29,35,36,37],changelog:[21,25],channel:[19,25,26],charact:19,check:[3,4,5,9,10,11,15,19,28,33,34,35],check_onli:[3,4,23],check_vers:[4,15,19],checksum:[5,15,33],choic:10,chunk_siz:[1,3,4],ci:19,citadel:[29,33],citat:[1,19,20,25],cite:[19,25],clash:30,classic:34,climlab:19,close:31,cmd:26,cockett:19,code:[0,16,18,19,24,25,27,29,34,37],codecov:19,collect:[19,32,36],com:[4,10,15,26,29,33,34,35,37],combin:[0,15],come:[0,18,19,26,35],command:[26,33],comment:[5,19,33],commit:[9,10,35],common:[23,30],commun:19,compar:[15,19,28,34],comparison:19,compat:[10,19,23,25,35],complet:31,compliant:9,compress:[2,15,19,22,30,36],comput:[15,19,28,30,34,37],conda:[19,26],condit:19,conduct:25,confer:0,configur:[19,28,34],conflict:35,connect:[3,5,6,10,33],consecut:19,conserv:21,consid:[5,20,23],consum:[5,19,30],contact:19,contain:[10,19,28,32,33,34,35],content:[2,11,19,22,33,35],continu:[19,35],contribut:[1,19,25],control:[0,10,15,35],conveni:[19,24,30,35],convent:[9,19],convers:[5,25,28,30],convert:[19,30],cope:19,copi:[5,15,19,22,33,34,35],copyright:19,correct:[19,32,34],correctli:[15,19],corrupt:[5,11,19,24,28,34],could:[18,19,24,30,32,35],count:31,cov:16,cover:[19,23],coverag:16,crash:19,creat:[1,4,5,15,19,24,27,28,29,31,32,35,36,37],creation:19,credenti:[4,18,23,32],cron:19,cronen:[29,32,33,35,37],cronenberg:[33,35],crucial:15,cryptograph:[0,19,24],csv:[15,18,23,24,28,29,32,33,35,37],current:[24,31,35,38],current_d:24,custom:[0,1,3,4,5,6,10,15,19,25,29,30,32,36],custom_fold:36,customiz:19,d908jdl:35,d:20,dagar:19,dai:24,daili:24,daniel:[19,20],danilo:19,data:[0,1,2,4,5,7,8,10,13,14,15,18,19,20,22,23,24,25,27,28,29,30,31,32,33,36,37],data_fil:[30,33],datafram:[18,30,32,35,36],dataset:[10,13,19,25,32,34,35],datavers:[1,19],date:24,datetim:24,decomp:[2,22],decompress:[0,15,19,25,28,30,34,35],dedic:19,def:[18,22,23,30,31,32,35,36],defin:[2,14,19,25,28,29,35],delai:19,deleg:19,delet:[15,19,24],depend:[0,10,15,19],dependent:19,deprec:[19,21],design:0,desir:30,detail:[1,4,5,15,32],detect:[23,32,35],determin:[1,2,5],dev:[9,35],develop:[0,10,19,21,26,35,38],devis:0,dict:[5,10],dictionari:[5,10,29,33],didn:[2,7,8],differ:[15,19,21,22,24,26,29,30,33,34,35],digit:[0,1],directli:[5,17,18,30,31,36],directori:[13,14,19,24,33],dirnam:29,disabl:19,disallow:35,discov:16,disk:[2,15,30],displai:[19,31],distribut:[18,26,35],disturb:28,do9iwd:10,doc:[16,19,35],docstr:19,doctest:16,document:[14,19,34,35],doe:[10,23,28,34],doesn:[1,3,4,5,15,18,19,23,24,28,29,30,34,35],doggo:4,doi:[0,1,5,19,20,23,34],doidownload:32,domin:19,don:[19,28,29,30,33,34,35],done:15,doubt:38,download:[0,1,2,3,4,5,6,7,8,10,12,15,18,19,22,24,25,26,27,31,33,36],download_auth:18,download_ftp:18,downstream:19,drop:[19,21],due:[19,35],durant:19,durbin:19,dure:[0,5,16],e32b18dab23935bc091c353b308f724f18edcb5:33,e9592cb46cf3514a1079051f8a148148:15,e:[3,6,20,23,32],each:[0,5,10,15,24,33,35,36],earlier:33,easi:[0,19,30],easier:[20,30],easili:[2,19,35],effici:[5,30],effort:20,egor:19,either:[2,19,30],element:5,els:[34,37],emit:35,empti:[3,6,29],enabl:[19,31],encourag:10,end:[10,15,29,31],enforc:33,enhanc:19,enough:[19,23],ensur:[15,21,24],entir:[19,21,29,32,35],entri:[5,10,20,30,33],env:[10,37],environ:[0,10,14,18,23,35,37],error:[1,3,4,5,6,10,15,16,19,27,31,32,35],etc:[5,12,15,30,34,35],even:[5,10,15,28,32,34],event:[12,27],everi:[10,15,19,29],everyon:0,everyth:[1,3,4],everywher:28,ex:26,exact:[15,34,36],exactli:[29,35],exampl:[1,2,3,4,6,9,10,11,15,16,18,19,22,23,24,28,29,30,31,32,33,34,35,36],except:[4,5,15,19,24],exchang:2,exclud:19,execut:[5,10,19,26,27,30],exist:[0,1,2,3,4,5,7,8,10,15,19,22,23,24,27,28,30,34,35],expand:14,explain:19,explicitli:18,explor:33,expos:19,extend:0,extens:[0,2],extern:19,extra:16,extract:[7,8,19,30,36],extract_dir:[7,8,36],extrem:19,f:[1,4,11,15,31,33],facilit:19,fact:29,factor:[19,35],fail:[5,10,16,19,24],failur:[19,35],fake:4,fallback:[4,9,15],fals:[1,3,4,5,6,10,15,16,19,23,35],fast:30,faster:26,fatiando:[0,4,10,15,19,25,26,34],favor:[5,10],feasibl:19,featur:[0,5,19,31,35],fetch:[1,2,3,4,5,6,7,8,10,18,19,20,22,23,24,25,29,30,31,32,34,35,36],fetch_and_unpack_tar_fil:36,fetch_c137:[18,32,35],fetch_compressed_fil:22,fetch_cronen:35,fetch_protected_data:[18,23],fetch_tiny_data:32,fetch_zipped_arch:36,fetch_zipped_fil:[30,36],fictiti:33,figshar:[0,1,19,32,34],fikl:19,file:[0,1,2,3,4,5,6,7,8,10,11,12,13,15,18,19,20,22,23,24,25,27,30,31,32,36],file_hash:[19,24,33],file_path:[28,32,34],filenam:[19,24,33],fileobj:5,fill:5,filter:[19,21],find:[1,35],first:[10,15,19,33,34,35],fist:19,fix:[19,30],flag:19,flamig:19,florian:19,fname2:15,fname:[2,5,7,8,10,11,15,18,22,23,30,31,32,33,35,36],fnames_and_url:33,folder:[5,7,8,10,14,15,19,24,28,29,34,35,36],follow:[14,21,23,28,30,32,33,35],forg:26,forget:35,forgot:35,format:[1,4,5,10,15,19,23,30,32,35],forward:4,fraction:19,freeli:20,friend:20,friendli:[19,30],from:[0,1,4,5,7,8,10,12,13,15,17,18,19,21,22,23,24,25,26,27,29,30,31,32,34,36],front:19,frustrat:35,ftp:[0,1,3,15,19,23,24,28,32,34,35],ftpdownload:[18,19],ftplib:[3,19],fu:19,full:[2,5,7,8,15,19,30,34,35],full_path:[5,15,30],fulli:19,further:19,futur:15,g:32,gabriel:19,garag:32,gemgi:19,gener:[19,24,28,33],geneviev:19,get:[1,4,5,12,15,18,19,23,24,27,28,33,34,35],get_logg:[19,27],get_url:[5,19],git:[26,35],github:[4,10,15,19,25,26,29,33,34,35,37,38],give:[21,33,36],given:[1,2,3,4,5,6,7,8,10,11,13,15,19,23,35],global:10,goe:20,good:[5,10,18,28],goodboi:[4,18,22,23,24,30,36],googl:19,got:0,gov:24,grammar:19,graviti:28,great:19,gregor:19,guarante:[15,34],guid:19,guidanc:21,gz:[2,15,22,36],gzip:[2,15,19,22],h5:31,h:20,ha:[0,5,9,10,11,15,19,22,23,30,33,35],hand:33,handl:[4,19,27,28,30,33,34,35],happen:[15,19,28,30,34,35],hard:[18,37],harfouch:19,harrington:19,hash:[0,5,10,11,13,15,19,25,26,28,32,33,35],hashlib:24,hauser:19,have:[5,10,13,15,16,19,22,23,26,28,29,30,31,32,33,34,35],help:[13,19,20,25,34,35],here:[0,20,25,31],highli:[10,15,33,34],histolab:19,home:24,hook:[19,30],horta:19,host:[10,29,32,33],how:[24,25,27],howev:[28,33],hoyer:19,html:19,http:[0,1,4,10,15,19,20,23,26,28,29,31,32,33,34,35,37],httpbin:4,httpdownload:[1,18,19,23,31],hugo:[19,20],i:[3,6,19,23,31],icepack:19,idea:18,ideal:15,identifi:[0,1],ignor:[5,10,35],imag:19,implement:[23,30,31],improv:19,inabl:27,includ:[0,2,5,15,19,21,23,29,30,32,33,35,37],incompat:19,increas:[5,10,19,24],increment:19,independ:29,indic:[2,3,4,6,7,8,16,23,35],individu:[5,10,19,28,29,30],info:27,inform:[5,16,19,28,32,35],initi:[5,10],insert:10,insid:17,instal:[1,3,4,5,6,10,15,18,19,25,31,32],install_requir:33,instanc:[1,2,3,4,5,6,7,8,23,28,30,31,35],instead:[1,2,3,4,5,7,8,15,19,21,22,24,29,32,34,35,37],instruct:33,integ:31,integr:[0,19,33,35],intend:5,interact:1,intercept:30,interdimension:35,intern:[17,19],interpret:[7,8,10,15],introduc:[19,21],invalidvers:9,ipython:19,is_avail:[5,19,23],isn:[19,28,34],issu:[0,1,19,21,30,32],item:33,its:[5,15,19,24,33,37],itself:2,j:20,jan:20,job:[19,35],john:[19,20],join:[10,25,29],joss:[19,20],journal:[19,20],jupyt:[19,28,34],just:[0,2,15,19,25,36],justifi:20,kacper:19,keep:[13,22,33],kei:[5,10,29],kemenad:[19,20],kempf:19,kept:[30,35],keyword:[1,4,23],king:19,know:[18,24,28,33,34],known:[15,30,35],known_hash:[15,19,24,31,32,33,34],kowalik:19,kwarg:[1,4],kyle:19,l:20,larg:[5,19,22,30,31,33,35,36],larger:19,last:[9,15,19,21],later:33,latest:[19,26,38],latex:20,layout:[19,35],lazi:19,least:35,leeman:[19,20],len:15,leonardo:[19,20],less:19,let:[18,30,35,36],level:[10,12,17,19],librari:[0,1,4,10,14,19,35],licens:19,light:33,like:[0,1,3,4,5,12,18,19,23,27,30,31,32,34,35],limit:19,line:[4,5,19,29,33],link:[1,10,19,38],lint:19,list:[7,8,10,15,19,21,23,24,25,30,32,33],ll:[4,10,24,26,28,33,35],load:[1,3,4,5,18,19,22,28,30,32,33,35,36],load_registri:[5,19,29,33],load_registry_from_doi:[5,19,32],loadtxt:32,local:[0,1,2,3,4,5,6,7,8,10,14,15,19,23,24,28,29,30,33,35,37],locat:[0,7,8,14,15,19,25,28,34,35,36],log:[12,15,19,21,25,34],logger:[12,27],login:[3,4,6,18,19,23,32],logo:19,longer:[19,24],look:[0,13,18,31,35],lost:[2,22],lot:19,low:21,lowercas:19,luca:19,ludwig:19,luke:19,lzma:[2,15,19],m9:[1,32],m:[20,26],mac:14,machin:34,made:[5,10,19,20],mai:[17,19,24,35,36],main:[4,15,19,29,30,32,33,35,37],maintain:[20,35],mainten:19,major:21,make:[0,4,5,10,13,15,19,20,23,28,30,32,33,34,35],make_registri:33,makedir:33,malform:19,manag:[0,1,3,4,5,6,10,15,19,25,26,28,34],mani:[5,13,33],manifest:33,manual:[13,19,26,32,33],map:28,marcolini:19,mark:[9,10,19,21,35],markdown:19,martin:19,master:[9,10,19],match:[5,15,19,24,28,34,35],mathia:19,matthew:[19,20],maximum:[5,10],mccloi:19,md5:[15,19,24,31,32,33,34],md:[19,35],mean:[3,6,10,19,35],meant:[15,17],member:[7,8,19,30,36],memori:[1,3,4,5,30],mention:19,merg:35,mess:[19,25],messag:[5,19,21,27,34],metadata:[24,30],method:[1,2,3,4,5,6,7,8,12,15,19,23,24,27,30,31,32,35],metpi:0,mi:20,might:[19,24,30],miniconda:26,minim:[0,30,31],minimal_progress:31,minimalprogressdisplai:31,minimum:[19,21],minor:[19,21],mirror:19,mismatch:[5,10,19,35],miss:30,mix:19,mne:19,mode:19,modifi:30,modul:[3,10,17,19,21,27,28,29,35],moment:31,month:[20,21],more:[2,5,19,27,28,30,32,34,35],most:[26,27,35],mostli:17,move:[17,19],much:19,multipl:[0,10,25,28,34,35],must:[1,5,6,7,8,10,19,23,28,30,31,32,33],my:28,my_password:18,my_usernam:18,myd7349:19,mydataserver_password:18,mydataserver_usernam:18,mydownload:23,mypooch:[10,23],myprocessor:30,myproject:10,myproject_allow_upd:35,myproject_data_dir:10,n:33,name:[1,2,3,4,5,6,7,8,10,11,13,14,15,19,22,23,28,29,30,32,33,34,35,36,37],namespac:[17,19],napari:19,nc:[28,31],ncar:19,ncc:24,necess:0,necessari:[0,10,19,30],need:[0,1,3,4,5,6,10,13,15,18,19,21,22,23,24,25,26,28,29,30,31,32,34,35],nep29:21,netcdf:28,network:[19,35],never:15,newer:21,next:34,nlst:19,non:[16,18,19,23],none:[1,2,3,4,5,6,7,8,10,15,23,24,29,31,32,33,34,35],not_env:10,not_from_env:10,note:[2,19,21],notebook:[19,28,34],notic:[1,19,32,33,35],notimplementederror:23,now:[19,22,24,29,30,31,35,36],npy:22,number:[5,10,19,20,33,35,37],numpi:[22,32],numpydoc:19,obejct:23,object:[0,1,3,4,5,6,12,15,19,23,27,30,31],obtain:28,occur:35,odi:28,off:31,offend:19,offer:0,offici:19,offlin:19,often:1,old:19,older:[19,21,26],omit:[19,24,29,35,36],onc:[0,5,10,19,24,28,30,34,35],one:[0,1,2,3,4,5,15,19,24,27,28,31,32,34,35,36],ones:21,onli:[0,1,3,4,5,6,7,8,10,15,19,23,24,27,28,30,31,33,34,36],open:[1,2,4,5,6,11,15,19,20,30,33],open_dataset:28,openssl:24,oper:[3,6,10,14,15,19,23,28,35,36],option:[2,10,19,22,23,26,28,35,36],order:[23,30,31],org:[4,19,20,28,31,32],organ:17,origin:[23,30],os:[1,4,10,11,15,18,19,22,23,29,30,33],os_cach:[10,15,19,24,28,29,32,33,34,35,37],other:[1,15,19,23,25,26,30,31,35],otherwis:[2,3,4,5,6,7,8,9,10,23,28],our:[1,15,19,20,21,23,25,28,30,31,32,33,35,36],ourselv:30,out:[0,1,19,21,35],outag:35,outdat:[2,7,8,30],output:[1,2,3,4,6,7,8,13,30],output_fil:[1,3,4,6,23],outsid:[31,35],over:[1,3,4,6,18,19,24,32,33],overwrit:[10,15,19,37],overwritten:[2,22],own:[18,19,33],packag:[0,14,17,19,24,25,26,34],package_data:33,page:[19,20,23],panda:[18,23,30,32,35,36],panel:19,panfilov:19,paper:[19,20],parallel:19,paramet:[1,2,3,4,5,6,7,8,9,10,11,13,14,15,16,23,30,36],paramiko:[6,18,26,32],part:[10,15,19,25],partial:35,particular:1,particularli:[19,30],pass:[1,2,4,5,10,15,18,19,22,23,30,31,35,36],password:[3,4,5,6,18,23],past:[15,34,35],patch:19,path:[1,2,3,4,5,6,7,8,10,14,15,19,23,24,28,29,30,32,33,34,35,36,37],pathlib:[10,14],pathlik:[10,15],pep440:[9,10],per:5,perform:[5,19,30],period:[19,24],permanenetli:35,permiss:35,pesc:19,philip:19,pin:[19,21],pip:[19,26],pkg_resourc:[19,33],place:[10,28,29,30,34],plan:19,platform:19,platformdir:[14,19,26],plausibl:19,pleas:[17,19,20,21],plug:0,plumbu:[29,32,33,35,37],plumbus_data_dir:37,point:[1,19,32],pooch:[17,18,19,22,23,24,26,27,28,29,30,31,32,33,34,35,36,37],poorli:30,popul:[5,19,29,32],port:[3,6,18,19],portal:35,posit:31,possibl:[5,19,21,24,28,30],post:[0,5,15,19,25],power:19,practic:35,prefer:35,prefix:33,prepend:[5,15,24,33],present:[0,24,29],prevent:19,previou:[19,21],previous:19,print:[1,3,4,5,6,10,11,15,16,23,24,25,26,27,34],prioriti:[19,21],privaci:19,privat:19,probabl:18,problem:[19,21,24],process:[0,5,19,30,33],processor:[2,5,7,8,15,19,22,25,27,28,34,36],progress:[1,3,4,5,6,15,19,23,25,26,28,34],progressbar:[1,3,4,5,6,15,31],project:[0,10,14,19,20,25,28,29,33,35],prompt:26,prone:19,properli:19,properti:19,protocol:[0,5,15,19,23,25,34,35],provid:[1,4,5,7,8,15,18,19,23,24,30,32,35,36],publish:[19,20],pull:35,pup:[10,30],pure:0,purpos:[1,4,15],push:19,put:13,pw9co2iun29juoh:15,py:[16,19,28,33,35],pypi:19,pytest:[16,19],python:[0,19,23,24,25,28,30,33,34,35],r:[20,30,31],race:19,rais:[5,9,15,16,19,23,24,32,35],rampin:[19,20],raw:[4,10,15,29,33,34,35,37],re:[2,7,8,10,19,20,26,35],read:[1,4,15,19,30,33,35],read_csv:[18,23,30,32,35,36],readm:[19,35],real:19,realiz:0,receiv:30,recent:19,recogn:19,recommend:[15,19,21,26,28,33,34,35],record:[5,10,12],recurs:13,redirect:23,redirect_download:23,reduc:22,refactor:19,refer:19,reflect:38,registri:[5,10,13,19,24,25,30,32,34,35,37],registry_fil:[10,19,33],rel:[2,5,7,8,10,13,30],releas:[9,10,19,21,35,38],reli:[19,21],remain:19,remi:19,remot:[5,10,19,24,28,35],remov:[1,4,11,15,19],renam:19,render:[19,31],repeat:[28,34,35],repeatedli:35,replac:[19,21],repositori:[0,1,4,5,15,19,32,34,35],repres:31,reproduc:[15,34],request:[1,4,5,10,19,23,25,26,35],requir:[1,3,4,5,6,15,16,18,19,21,23,26,29,35],research:[0,19,20,25],reset:31,resource_stream:[19,33],rest:32,restrict:19,result:[5,10,31,34,35],retain:[19,21],retri:[5,10,19],retriev:[0,1,2,3,4,5,6,7,8,19,23,24,25,30,31,32,33],retry_if_fail:[5,10,19,35],review:[19,20],revis:19,rick:[29,33,35,37],right:[5,15,19],rob:19,robert:19,rose:19,rowan:19,rst:19,rstrip:4,run:[0,15,16,19,24,30,34,35],ryan:19,s:[0,1,5,10,15,18,19,20,23,24,25,27,28,30,31,33,34,36],safe:[10,19,30,35],sai:[27,29,30,35,36],same:[0,1,7,8,15,19,24,28,29,30,31,32,33,34,36],sampl:[0,18,19,25,30,32,33,34,36],sandbox:[19,34],santiago:[19,20],sarthakjariwala:19,save:[6,15,19,28,30,34],saw:35,scenario:33,scienc:28,scientist:[0,20],scikit:19,scipi:[0,19],scm:19,screen:15,script:[24,28,34],seaborn:19,second:[3,6],section:[19,28,35],secur:[15,18,19,24,33],see:[1,2,4,5,14,15,19,23,26,28,30,32,33,34,35],seen:19,select:19,self:31,sensibl:10,separ:[1,5,10,19,24,28,29,31,32,35],serv:28,server:[3,4,5,6,18,19,23,24,30,31,32],servic:19,set:[2,10,18,19,21,24,28,29,31,33,34,35],setlevel:[12,27],setup:[19,29,31,32,33],setuptool:19,sever:[15,21,28,34],sftp:[0,6,19,23,26,28,32,34,35],sftpdownload:[18,19,26],sha1:[15,33,34],sha256:[5,11,15,19,24,28,34,35],sha512:24,sha:19,shapero:[19,20],share:[0,19,33],ship:33,should:[2,5,10,15,17,19,23,26,29,31,33,34,37],show:[28,34],side:29,sign:5,signatur:23,silenc:[19,21],similar:35,similarli:[30,36],simpli:0,sinc:[1,17,19,29,32,33,34],singl:[0,15,19,25,28,30,31,35,36],site:[23,29,33],sitelist:24,situat:[26,30],size:[24,30,35],slack:19,slash:19,slow:22,snippet:19,so:[0,1,2,4,7,8,10,15,18,19,24,27,29,30,32,35,37],socket:[3,6],soft:19,softwar:[19,20],soler:[19,20],solv:24,some:[0,3,10,16,18,19,23,29,31,33],someon:34,somesite_password:[18,23],somesite_usernam:[18,23],someth:[18,30,31,37],sometim:[5,18,19,23,24,30,35],somewebpag:28,sophist:27,sourc:[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,19,20,24,25,33,34,35],space:[2,5,19,22,29,30],specif:[19,33,36,38],specifi:[1,2,3,4,5,6,10,15,19,22,23,28,29,32,33,34,35],speed:[2,19,22],spell:19,sphinx:19,splitext:15,sporad:[19,35],sprint:0,stabil:19,stabl:[1,19],standard:[1,2,3,4,5,6,15,19,23,28,31,35],start:[0,16,19,34],station:24,statu:[5,19],stderr:[1,3,4,5,6,15,31],stephan:19,still:[5,19,21,29,35,37],storag:[2,5,7,8,10,15,19,22,28,29,30,35,37],store:[7,8,10,19,24,28,32,33,35,36],str:[1,2,3,4,5,6,7,8,9,10,11,13,14,15,23,30,31],stream:[1,3,4,31],string:[3,6,9,10,29,35],strip:[1,4,15],style:[5,10,19,28],subclass:19,subdirectori:[5,10,13,24,28,36],subfold:[10,19],submodul:17,substitut:35,success:35,suffix:[7,8,36],suggest:1,suit:16,summari:[1,2,3,4,5,6,7,8],support:[1,2,3,5,18,19,23,25,26,28,29,32,34],sure:[0,15,19,28,30,33,34,35],sy:31,system:[5,10,14,15,19,28,35],t:[1,2,3,4,5,7,8,15,18,19,21,23,24,28,29,30,33,34,35],tag:35,take:[2,19,23,30,31],taken:[2,5,7,8,30],tar:[2,7,15,19,22,30,36],task:30,tediou:33,tell:[19,36],temperatur:28,temporari:[15,19],temporarili:35,tend:19,termin:[24,26,27],terra:[0,25],test:[0,1,4,11,13,15,19,32,34],text:[19,33],than:19,thei:[5,10,15,19,21,24,29,30,34,35],them:[5,15,19,23,28,30,32,33,35,37],theme:19,thi:[0,1,2,3,4,5,6,7,8,9,10,12,13,15,18,19,20,21,22,23,24,27,29,30,31,32,33,34,35,37],thing:[1,19,20],third:5,through:[0,5,15,18,19,33],throughout:19,time:[1,2,3,4,5,10,18,19,21,22,24,30,32,34,35],timeout:[3,6],tini:[1,4,15,32,34],titl:20,togeth:0,tool:35,top:[0,17,19],total:31,tqdm:[1,3,4,5,6,15,19,26],trade:[22,30],trail:[10,19],train:35,travisci:19,trick:1,trigger:[10,15],trust:33,tupl:10,turk:[19,20],turn:31,tutori:[0,19,35],two:[19,33,34],txt:[1,4,10,11,15,19,29,30,32,33,34,36],type:[11,32],typo:19,tyrant:37,uieda2020:20,uieda:[19,20],unabl:28,unaffect:21,unchang:19,under:[19,35],understand:24,uniqu:[15,19],unix:[5,10,14,28],unless:24,unpack:[2,7,8,15,19,25,27,28,30,34,35],unpack_to_custom_dir:36,unread:19,unreleas:[9,26],untar:[2,15,19,36],until:[5,10,19],unwant:19,unzip:[0,2,5,12,15,19,30,36],up:[19,24,28],updat:[2,5,7,8,10,13,15,19,21,24,27,28,30,31,33,34],upgrad:26,upon:0,upstream:19,url:[1,3,4,5,6,10,15,19,20,23,25,28,31,32,33,34,35],urllib:25,us:[1,2,3,4,5,6,7,8,9,10,11,12,14,15,16,18,19,20,21,22,23,24,25,26,27,29,30,32,33,35,36,37,38],usag:19,user:[0,3,4,6,10,14,18,19,21,23,24,25,30,34,35],user_cache_dir:14,usernam:[3,4,5,6,18,23],usual:[1,3,4,14,30,35,37],util:[0,19],v0:[10,19,35,38],v1:[1,19,21,32,33,34,35,38],v3:19,valentino:19,valid:35,valu:[5,10,14,15,23,24,28,29,30,35,37],van:[19,20],variabl:[0,10,14,18,19,35,37],vast:21,verbos:[12,16,25],veri:[19,35],verif:0,verifi:[0,15],version:[0,2,9,10,15,22,25,26,28,29,30,32,33,34,36,37],version_dev:[10,29,32,33,35,37],virtual:10,volum:20,w:[11,33],wa:[0,2,4,5,7,8,15,19,21,35],wai:[18,24,26,27,28,29,30,35,37],wait:[5,10,19],want:[0,1,3,4,6,10,19,23,24,25,29,30,33,34,35,36],warn:[5,19,21,24,27],wasn:[2,7,8,19],wb:30,we:[1,4,10,15,19,21,24,28,29,30,32,33,35,36,37],weather:24,websit:4,weekli:19,weight:33,welcom:1,well:[10,16,19,35],wellmann:19,were:[19,32],what:[2,7,8,9,30,35],whatev:4,wheel:35,when:[1,3,4,5,6,10,15,18,19,24,26,29,30,33,35,37,38],whenev:21,where:[10,15,24,33,36],whether:[5,10,30],which:[1,3,4,6,7,8,19,22,23,24,30,33,35,36],who:[0,19,21],why:25,window:[5,10,14,19,26,28],wish:[24,27],within:30,without:[3,4,5,19,21,23,25,28,34],won:[15,21],word:31,work:[1,5,18,19,21,25,31,35],workflow:19,worri:34,worth:30,would:[0,5,10,15,19,24,30,35],wrapper:19,writabl:19,write:[11,19,30,33],wrong:19,www:[28,29,33],xarrai:28,xdg:19,xdg_cache_hom:14,xr:28,xx:10,xxh128:24,xxh128sum:24,xxhash:[19,24,26],xxxxx:10,xxxxxx:34,xz:[2,15,19],year:20,yet:3,you:[1,3,4,5,6,10,13,15,19,20,21,22,23,24,26,27,28,29,30,31,32,33,34,35,36,37],your:[0,10,13,15,18,19,20,24,28,29,33,34,35],yourself:15,zac:19,zenodo:[0,1,19,32,34],zero:16,zip:[2,7,8,15,19,22,24,30,32,36],zip_fil:30,zipfil:30},titles:["Why use Pooch?","pooch.DOIDownloader","pooch.Decompress","pooch.FTPDownloader","pooch.HTTPDownloader","pooch.Pooch","pooch.SFTPDownloader","pooch.Untar","pooch.Unzip","pooch.check_version","pooch.create","pooch.file_hash","pooch.get_logger","pooch.make_registry","pooch.os_cache","pooch.retrieve","pooch.test","List of functions and classes (API)","Authentication","Changelog","Citing Pooch","Version compatibility","Decompressing","Downloaders: Customizing the download","Hashes: Calculating and bypassing","Home","Installing","Logging and verbosity","Fetching files from a registry","Multiple download URLs","Processors: Post-download actions","Printing progress bars","Download protocols","Registry files","Retrieving a single data file","Manage a package\u2019s sample data","Unpacking archives","User-defined cache location","Documentation for other versions"],titleterms:{"0":19,"1":19,"2":19,"3":19,"4":19,"5":19,"6":19,"7":19,"8":19,"case":0,"class":17,"function":17,The:35,action:30,adjust:27,algorithm:24,api:17,archiv:36,authent:18,avail:23,backward:21,bar:31,basic:[34,35],bypass:24,cach:37,calcul:24,chang:21,changelog:19,check:[23,24],check_vers:9,cite:20,commun:25,compat:21,content:25,core:17,creat:[10,23,30,33],custom:[23,28,31,34],data:[34,35],decompress:[2,22],defin:37,depend:[21,26],digit:32,disabl:35,document:[25,38],doi:32,doidownload:1,download:[17,23,28,29,30,32,34,35],fail:35,fetch:28,file:[28,29,33,34,35],file_hash:11,format:33,from:[28,33,35],ftp:18,ftpdownload:3,get:25,get_logg:12,go:35,hash:[24,34],here:35,histori:0,http:18,httpdownload:4,identifi:32,incompat:21,instal:26,level:27,list:17,locat:37,log:27,make_registri:13,manag:35,method:28,miscellan:17,multipl:29,object:32,os_cach:14,other:[24,38],own:[23,30],packag:[33,35],pooch:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,20,21,25],post:30,print:31,problem:35,processor:[17,30],progress:31,protocol:32,python:[21,26],refer:25,registri:[28,29,33],remot:33,retri:35,retriev:[15,34],s:35,sampl:35,setup:[28,35],sftp:18,sftpdownload:6,singl:34,specifi:24,start:25,support:[21,24],tabl:25,test:[16,35],thi:28,tqdm:31,train:25,unknown:34,unpack:36,untar:7,unzip:8,updat:35,url:29,us:[0,28,31,34],usag:[29,33,34],user:37,util:17,verbos:27,version:[19,21,35,38],when:34,where:35,which:26,why:[0,28],your:[23,25,30]}}) \ No newline at end of file diff --git a/v1.8.0/unpacking.html b/v1.8.0/unpacking.html new file mode 100644 index 00000000..25ac3835 --- /dev/null +++ b/v1.8.0/unpacking.html @@ -0,0 +1,555 @@ + + + + + + + + + + Unpacking archives | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

Unpacking archives

+ +
+
+ +
+
+
+ +
+ +
+

Unpacking archives

+

Let’s say our data file is actually a zip (or tar) archive with a collection of +files. +We may want to store an unpacked version of the archive or extract just a +single file from it. +We can do both operations with the pooch.Unzip and +pooch.Untar processors.

+

For example, to extract a single file from a zip archive:

+
from pooch import Unzip
+
+
+def fetch_zipped_file():
+    """
+    Load a large zipped sample data as a pandas.DataFrame.
+    """
+    # Extract the file "actual-data-file.txt" from the archive
+    unpack = Unzip(members=["actual-data-file.txt"])
+    # Pass in the processor to unzip the data file
+    fnames = GOODBOY.fetch("zipped-data-file.zip", processor=unpack)
+    # Returns the paths of all extract members (in our case, only one)
+    fname = fnames[0]
+    # fname is now the path of the unzipped file ("actual-data-file.txt")
+    # which can be loaded by pandas directly
+    data = pandas.read_csv(fname)
+    return data
+
+
+

By default, the Unzip processor (and similarly the +Untar processor) will create a new folder in the same location +as the downloaded archive file, and give it the same name as the archive file +with the suffix .unzip (or .untar) appended.

+

If you want to change the location of the unpacked files, you can provide a +parameter extract_dir to the processor to tell it where you want to unpack +the files:

+
from pooch import Untar
+
+
+def fetch_and_unpack_tar_file():
+    """
+    Unpack a file from a tar archive to a custom subdirectory in the cache.
+    """
+    # Extract a single file from the archive, to a specific location
+    unpack_to_custom_dir = Untar(members=["actual-data-file.txt"],
+                                 extract_dir="custom_folder")
+    # Pass in the processor to untar the data file
+    fnames = GOODBOY.fetch("tarred-data-file.tar.gz", processor=unpack)
+    # Returns the paths of all extract members (in our case, only one)
+    fname = fnames[0]
+    return fname
+
+
+

To extract all files into a folder and return the path to each file, omit the +members parameter:

+
def fetch_zipped_archive():
+    """
+    Load all files from a zipped archive.
+    """
+    fnames = GOODBOY.fetch("zipped-archive.zip", processor=Unzip())
+    return fnames
+
+
+

Use pooch.Untar to do the exact same for tar archives (with optional +compression).

+
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/user-defined-cache.html b/v1.8.0/user-defined-cache.html new file mode 100644 index 00000000..4429e5b6 --- /dev/null +++ b/v1.8.0/user-defined-cache.html @@ -0,0 +1,517 @@ + + + + + + + + + + User-defined cache location | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

User-defined cache location

+ +
+
+ +
+
+
+ +
+ +
+

User-defined cache location

+

The location of the local storage cache in the users’ computer +is usually hard-coded when we call pooch.create. +There is no way for them to change it to something else.

+

To avoid being a tyrant, you can allow the user to define the cache location +using an environment variable:

+
BRIAN = pooch.create(
+    # This is still the default
+    path=pooch.os_cache("plumbus"),
+    base_url="https://github.com/rick/plumbus/raw/{version}/data/",
+    version=version,
+    version_dev="main",
+    registry={
+        "c137.csv": "19uheidhlkjdwhoiwuhc0uhcwljchw9ochwochw89dcgw9dcgwc",
+        "cronen.csv": "1upodh2ioduhw9celdjhlfvhksgdwikdgcowjhcwoduchowjg8w",
+    },
+    # The name of an environment variable that can overwrite the path
+    env="PLUMBUS_DATA_DIR",
+)
+
+
+

In this case, if the user defines the PLUMBUS_DATA_DIR environment +variable, Pooch use its value instead of path.

+

Pooch will still append the value of version to the path, so the value of +PLUMBUS_DATA_DIR should not include a version number.

+
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/v1.8.0/versions.html b/v1.8.0/versions.html new file mode 100644 index 00000000..80bf953a --- /dev/null +++ b/v1.8.0/versions.html @@ -0,0 +1,517 @@ + + + + + + + + + + Documentation for other versions | Pooch v1.8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + +
+ + + + + + + + + + + + + +
+ + +
+ +
+
+
+
+
+ +
+

Documentation for other versions

+ +
+
+ +
+
+
+ +
+ +
+

Documentation for other versions

+

Use the links below to access documentation for specific versions +(when in doubt, use the latest release):

+ +
+ + +
+ + + + + +
+
+
+

+ + © Copyright 2023, The Pooch Developers.
+ Last updated on Oct 24, 2023.
+

+
+
+ + +
+
+ + + + + \ No newline at end of file